Comment: | add janet API bootstrapping infra (prim, core), begin building executor (vm.c), design class model, continue working on loading, add script instances, tidy up src organization, build janet to use godot malloc/free |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
91e02e35d553b5de0f64342b5a954a53 |
User & Date: | lexi on 2025-02-21 22:02:29 |
Other Links: | manifest | tags |
2025-02-28
| ||
00:10 | continue iterating on object model check-in: c0fd81ac3d user: lexi tags: trunk | |
2025-02-21
| ||
22:02 | add janet API bootstrapping infra (prim, core), begin building executor (vm.c), design class model, continue working on loading, add script instances, tidy up src organization, build janet to use godot malloc/free check-in: 91e02e35d5 user: lexi tags: trunk | |
2025-02-10
| ||
01:11 | add automatic instance bindings with accessors check-in: 351bb17bed user: lexi tags: trunk | |
Modified gdjn.ct from [af1bbb3860] to [a79a150ec2].
6 6 7 7 %toc 8 8 9 9 @civ { 10 10 to meet the bare minimum standards necessary to qualify as [!civilized] to my mind, a language must: 11 11 * support compilation, whether to bytecode or machine code (excludes gdscript) 12 12 * parse into an AST (excludes bash, php (or used to)) 13 - * support AST-rewriting macros (excludes everything outside the lisp family except maybe rust) 13 + * support AST-rewriting macros (excludes everything outside the lisp family except nim and maybe rust) 14 14 * use real syntax, not whitespace-based kludges (excludes python, gdscript inter alia) 15 15 * provide a proper module system (excludes C) 16 16 * provide for hygienic namespacing (excludes C, gdscript) 17 17 * provide simple, cheap aggregate types (excludes gdscript, java, maybe python depending on how you view tuples) 18 18 so, janet and fennel are about the only game in town. 19 19 } 20 20 ................................................................................ 50 50 abi: okay yes in reality it absolutely fucking does, everyone uses the same gods-damned amd one, in actual materially existing reality there is no interop problem at least on x86, but because we haven't ritually enshrined this model at ISO and pacified its marauding [!aramitama] with burnt offerings of motor oil and obsolete DRAM chips, we cover our eyes and pretend we cannot see what is sitting right in front of us. i love too compute dont u 51 51 52 52 so GDExtension is designed (if i may use the word liberally) with certain notions fixed firmly in mind. chief among them is that the actual C-level calls will be thoroughly ensconced in a suffocating buffer of template hackery, which will employ dark arts and crafts to generate gobs and gobs of impenetrable C at compile time. in fact, GDExtension is so designed that it is basically impossible to use it directly without a code generation layer. unwilling as i was to use C++ (it's against my religion), i wrote my layer from scratch, in janet. 53 53 54 54 this was not as bad as it could have been. janet has something [^deficient magnificent] in its stdlib that every language should have: a PEG module. the resulting program, ["tool/class-compile.janet] is under 1k lines! just barely. this tool is fired off by the makefile for the ["src/*.gcd] files, generating a header file ["gen/*.h] and an object file ["out/*.o] that implements the class described in the file. the care and feeding of this file format is described in the [>gcd GCD section]. 55 55 56 56 deficient: it is deficient in one particular way: it only operates over bytestrings. so you can use a PEG to parse raw text, or you can use it to implement a lexer, but you can't have both a PEG lexer and parser, which is really fucking dumb and makes things like dealing with whitespace far more painful than it needs to be.) 57 + 58 +##obj object model 59 +godot uses a strongly javalike OOP system, which translates poorly to Janet's prototype-based OO. gdjn therefore uses the following convention to translate between the two. 60 + 61 +the engine executes the "compile-time" phase of a janet script once it is finished loading. it is the responsibility of the compile-time thunk to set up the local environment to represent a class using def statements. the abstract serves as a proxy for method calls. when a lookup is performed against a class proxy, the proxy inspects its attached environment and returns an appropriate object. 62 + 63 +unadorned immutable bindings are treated as constants. values with the [":field type] annotation are treated as object fields. when a new instance is created, space is allocated for a field of the appropriate type, and initialized to the value of the binding. public [`var] mutable bindins are treated as static variables. 64 + 65 +~~~[janet] example bindings 66 +(def name "Lisuan") # constant string 67 +(def name :field "Lisuan") # field of type string, initializes to "Lisuan" 68 +(def name {:field :variant} "Lisuan") field of type variant 69 +(def- secret "swordfish") # private constant 70 +(def- secret :field "swordfish") # private string field 71 +(var count 0) # static variable of type int 72 +(var- count 0) # private static variable of type int 73 +~~~ 74 + 75 +unadorned functions are treated as static functions. 76 + 77 +private functions (those declared with [`def-]) are available only within the class implementation. they are not exported as methods. 78 + 79 +functions with the annotation [":method] are treated as methods. when invoked, they are passed a [`self]-reference as their first argument. 80 + 81 +function type signatures can be specified with the annotations [":takes [...]] and [`:gives [$type]]. 82 + 83 +tables with the annotation [":subclass] are treated as environment tables specifying inner classes. the macro [`subclass] should generally be used to maintain uniform syntax between outer and inner classes, e.g. 84 + 85 +~~~[janet] 86 +(use core) 87 +(use-classes Object RefCounted) 88 +(declare outerClass :is Object) 89 +(def val 10) 90 +(prop v vec3) 91 +(defclass innerClass :is RefCounted 92 + (def val 2)) 93 + 94 +# equivalent to 95 +(import prim) 96 +(def Object (prim/load-class :Object)) 97 +(def RefCounted (prim/load-class :RefCounted)) 98 +(def *class-name* :meta :outerClass) 99 +(def *class-inherit* :meta Object) 100 +(def val 10) 101 +(def innerClass (do 102 + (def *class-inherit* :meta RefCounted) 103 + (let [env (table/setproto @{} (curenv))] 104 + (eval '(do 105 + (def val 2)) 106 + env) 107 + env))) 108 +~~~ 109 + 110 +since the annotations are somewhat verbose, macros are provided to automate the process. 111 + 112 ++ janet + gdscript 113 +| ["(def count 10)] | ["const count := 10] 114 +| ["(def count {:field int} 10)] | ["var count: int = 10] 115 +| ["(defn open [path] ...)] | ["static func open(path: Variant) ...] 116 +| ["(defn open {:takes [:string] :gives :int} [path] ...)] | ["static func open(path: String) -> int: ...] 117 +| ["(defn close :method [me] ...)] | func close() -> void: ... 118 + 57 119 58 120 ##gcd GCD language 59 121 GCD is a simple IDL which is translated into [^complex much more complicated C]. it's designed to make writing GDExtension classes as close to the native GDScript experience as possible, without the syntactic hanging offenses. you define the structure of the class using GCD, and write inline C to implement your functions. linemarkers are emitted properly so when you inevitably fuck it up, the compiler will be able to apportion the blame properly. 60 122 61 123 complex: the generated implementation code is roughly 24x longer than the input file 62 124 63 125 the C "lexing" is pretty robust, and as long as you aren't doing horrible shit with the preprocessor (on the order of ["#define fi }]) it shouldn't choke on anything you write. if you [!are] doing horrible shit with the preprocessor however, [!you deserve everything you get].
Added lib/core.janet version [a791260fb2].
1 +# [ʞ] lib/core.janet 2 +# ~ lexi hale <lexi@hale.su> 3 +# 🄯 AGPLv3 4 +# ? provides a nicer interface to gdprims 5 +# > (import :/lib/core) 6 + 7 +(import /lib/prim) 8 + 9 +# this parameter controls the class to inherit from 10 +# when a superclass is not explicitly specified. 11 +# this should be set to point at RefCounted 12 +(def *class-base* (gensym)) 13 +(def *class-path* (gensym)) 14 + 15 +(def- valid-class-flags 16 + (do (def list [:abstract :final]) 17 + (tabseq [f :in list] f true))) 18 + 19 +(defn- assemble-meta [args] 20 + (defn recurse [acc dox t & r] 21 + (cond 22 + (string? t) 23 + (recurse acc [;dox t] ;r) 24 + (or (struct? t) 25 + (keyword? t)) 26 + (recurse [;acc t] dox ;r) 27 + [[;(if (empty? dox) [] 28 + [{:doc (string/join dox)}]) 29 + ;acc] t ;r])) 30 + (recurse [] [] ;args)) 31 + 32 +(defmacro class [name & dfn] 33 + (def def-map (tuple tuple (dyn *current-file*) 34 + ;(tuple/sourcemap (dyn :macro-form)))) 35 + (defn mapped-val [val] 36 + @{:value val 37 + :source-map def-map}) 38 + (var spec dfn) 39 + (var super (dyn *class-base*)) 40 + (var export-name (string name)) 41 + # documentation is stored differently for classes, 42 + # as godot needs to be able to retrieve the text 43 + # from a reference to @scope 44 + (def dox @[]) 45 + (def mode @{}) 46 + 47 + (forever 48 + (match spec 49 + [:is super_ & r] # set superclass 50 + (do (set super (eval super_)) 51 + (set spec r)) 52 + 53 + [:as ident & r] 54 + (do (set export-name (string ident)) 55 + (set spec r)) 56 + 57 + ([m & r] (has-key? valid-class-flags m)) 58 + (do (put mode m true) 59 + (set spec r)) 60 + 61 + ([x & r] (string? x)) # docstring 62 + (do (array/push dox x) 63 + (set spec r)) 64 + 65 + _ (break))) 66 + 67 + (def doc-str (string/join dox)) 68 + (def body spec) 69 + (with-dyns [*class-path* 70 + [;(or (dyn *class-path*) []) name]] 71 + (with-syms [$local-env $parent-env $form] 72 + ~(let [,$parent-env (or @scope (curenv)) 73 + ,$local-env (,table/setproto 74 + @{ '@name ,(mapped-val (keyword name)) 75 + '@gd-id ,(mapped-val (string export-name)) 76 + '@inherit ,(mapped-val super) 77 + '@mode ,(mapped-val mode) 78 + '@doc ,(if (empty? dox) nil 79 + (mapped-val doc-str))} 80 + ,$parent-env) 81 + ,name ,$local-env] 82 + (,put ,$local-env '@scope @{:value ,$local-env}) 83 + 84 + # *self* can be used to access statics 85 + (put ,$local-env '@self @{:value 86 + (fn @self[& x] 87 + (get-in ,$local-env [;x :value]))}) 88 + (each ,$form ',body 89 + (,eval ,$form ,$local-env)) 90 + ,$local-env)))) 91 + 92 +(defn- member [& x] 93 + (string/join [;(or (dyn *class-path*) []) 94 + ;x] ".")) 95 + 96 +(defmacro defclass* [id meta & r] 97 + ~(def ,id ,;meta (class ,id ,;r))) 98 + 99 +(defmacro defclass [id & r] ~(defclass* ,id [] ,;r)) 100 +(defmacro defclass- [id & r] ~(defclass* ,id [:private] ,;r)) 101 + 102 +(defn- parse-sig [sig] 103 + (match sig 104 + ([ty id dox & r] (string? dox)) 105 + [{:type (eval ty) :id id :doc dox} 106 + ;(parse-sig r)] 107 + [ty id & r] [{:type (eval ty) :id id} 108 + ;(parse-sig r) ] 109 + _ [])) 110 + 111 +(defmacro defm [id & rest] 112 + (def (meta ret sig & body) (assemble-meta rest)) 113 + (def args (parse-sig sig)) 114 + (def md [;meta :method {:sig args :ret (eval ret)}]) 115 + (def arg-names ['me ;(map (fn [{:id id}] id) args)]) 116 + (def path (symbol (member id))) 117 + ~(def ,id ,;md (fn ,path ,arg-names ,;body))) 118 + 119 +(defmacro defm- [id & r] ~(defm ,id :private ,;r)) 120 + 121 +(defmacro prop [id & args] 122 + (def (meta-list ty init-val) 123 + (assemble-meta args)) 124 + (assert init-val "property missing initial value") 125 + ~(def ,id :prop {:type ,ty} ,;meta-list ,init-val)) 126 + 127 +(defmacro const [id & args] 128 + (def (meta-list ty init-val) 129 + (assemble-meta args)) 130 + (assert init-val "constant missing value") 131 + ~(def ,id :const {:type ,ty} ,;meta-list ,init-val)) 132 + 133 +(defmacro hoist [ids & body] 134 + ~(upscope (def ,ids 135 + (do ,;body 136 + ,ids))))
Added lib/prim.janet version [36c3aeafd8].
1 +# [ʞ] lib/prim.janet 2 +# ~ lexi hale <lexi@hale.su> 3 +# 🄯 AGPLv3 4 +# ? declares the primitives supplied by gdjn 5 +# > (import /lib/prim) 6 + 7 +(def *gd-api-map* @{}) 8 + 9 +(defmacro- extern [name] 10 + (def sym (gensym)) 11 + (put *gd-api-map* sym name) 12 + ~(def ,name ',sym)) 13 + 14 +# takes a string and returns a prim/class-handle 15 +# for a native godot class 16 +(extern class-load) 17 + 18 +# an abstract that wraps a class and provides 19 +# a uniform interface for both native godot 20 +# classes and janet environment-classes 21 +(extern class) 22 +(extern class?) 23 +# @-prefixed methods/props are implemented by the vm. 24 +# everything else is passed through verbatim. 25 +# :@janet? - returns true if underlying class is 26 +# a janet env 27 +# :@methods - returns a list of methods 28 +# {:id method-id :args [(type/variant :Vector3) etc] 29 +# :ret (type/variant :String)} 30 +# :@super - returns a handle to the superclass 31 +# :@new - invokes a constructor, returning an object 32 +# use :@janet? and :@env methods to retrieve a 33 +# janet environment from a wrapped janet class 34 + 35 +(extern object) 36 +(extern object?) 37 +(extern object/class) 38 +# method calls on an object are translated to 39 +# godot method invocations. index into a class 40 +# to retrieve a property. 41 + 42 +# explicitly create godot values 43 +(extern type/dict) 44 +(extern type/array) 45 +(extern type/array-packed)
Modified makefile from [ab479c70d3] to [3ece26026f].
25 25 # form. 26 26 # 27 27 # * ["ext] is a Keter-class high-security containment unit for 28 28 # Other People's Code. 29 29 # 30 30 # * ["lib] contains (mostly janet) code that will be included 31 31 # as blobs in the binary. janet code will be able to import 32 -# them. these will be compiled down to .jimage files and 32 +# them. these will be compiled into the api.jimage file and 33 33 # then bundled into rsrc.o. libraries used in ["tool/*] 34 -# should only be placed in this directory if they are also 35 -# used at runtime in the godot environment (e.g. the OOP 36 -# macros). library code used only by tools belongs in the 37 -# tool directory. 34 +# should only be placed in ["lib] if they are also used at 35 +# runtime in the godot environment (e.g. the OOP macros). 36 +# library code used only by tools belongs in the tool 37 +# directory. 38 38 # 39 39 # * ["out] contains all live binaries and object files. 40 40 41 41 godot = godot4 42 42 godot.flags = --headless 43 43 godot.cmd = "$(godot)" $(godot.flags) 44 44 janet = $o/janet 45 45 git = git 46 46 git.flags.clone = --depth=1 47 47 48 48 janet.src.path = $x/janet 49 49 janet.src.git = https://github.com/janet-lang/janet.git 50 50 janet.root = $x/janet/src 51 -janet.cfg = 51 +janet.cfgfile = $s/janetconf.h 52 +janet.cfg = JANETCONF_HEADER=$(realpath janet.cfgfile) 53 +janet.cc.bin = -D_gdjn_shims 54 +janet.cc.lib = 52 55 53 56 cc.link = -flto 54 57 cc.comp = -fPIC 55 58 56 59 ifeq ($(debug),1) 57 - cc.link += -g 58 - cc.comp += -g -O0 60 + cc.link += -g 61 + cc.comp += -g -O0 -Wall -D_gdjn_build_debug 62 + ifndef feat.editor 63 + feat.editor = 1 64 + endif 65 +else 66 + cc.comp += -D_gdjn_build_release 67 +endif 68 + 69 +# are we building with editor support? 70 +ifeq ($(feat.editor),1) 71 + cc.comp += -D_gdjn_build_editor 59 72 endif 60 73 61 74 cc.gdjn.comp = $(cc.comp) \ 62 75 -std=gnu23 \ 63 76 -I"$g" \ 64 77 -I"$s" \ 65 78 -I"$(janet.root)/include" \ ................................................................................ 82 95 83 96 .PHONY: all clean purge 84 97 all: $o/gdjn.so 85 98 clean: 86 99 rm "$o/"*.o "$g/"*.{jimage,h} "$o/gdjn.so" 87 100 purge: 88 101 rm "$o/"* "$g/"* 102 + $(MAKE) -C "$(janet.src.path)" clean 89 103 90 104 %/:; $(path-ensure) 91 105 %: | $(@D)/ 92 106 93 107 tags: . 94 108 find "$s" "$g" -name "*.h" -o -name "*.c" | xargs ctags 95 109 96 -$o/gdjn.so: $o/gdjn.o $o/rsrc.o $o/interface.o \ 110 +$o/gdjn.so: $o/gdjn.o $o/rsrc.o $o/interface.o $o/vm.o \ 97 111 $o/janet-lang.o $o/janet-rsrc.o \ 98 112 $o/libjanet.a 99 113 "$(cc)" $(cc.gdjn.link) $^ -o"$@" 100 114 101 115 $o/interface.o: $t/c-bind-gen.janet \ 102 116 $g/interface.h \ 103 117 $(gd_api_spec) \ 104 - $(gd_api_iface) 118 + $(gd_api_iface) \ 119 + $(janet) 105 120 "$(janet)" "$<" loader | "$(cc)" $(cc.gdjn.comp) -c -xc - -o "$@" 106 121 107 122 $g/interface.h: $t/c-bind-gen.janet \ 108 - $(gd_api_spec) 123 + $(gd_api_spec) \ 124 + $(janet) 109 125 "$(janet)" "$<" header >"$@" 110 126 111 -$g/%.h: $s/%.gcd $t/class-compile.janet $(realpath $(janet)) 127 +$g/%.h: $s/%.gcd $t/class-compile.janet $(janet) 112 128 "$(janet)" "$t/class-compile.janet" "$<" header >"$@" 113 -$o/%.o: $s/%.gcd $g/%.h $t/class-compile.janet $(realpath $(janet)) 129 +$o/%.o: $s/%.gcd $g/%.h $s/gdjn.h $g/interface.h $t/class-compile.janet $(janet) 114 130 "$(janet)" "$t/class-compile.janet" "$<" loader \ 115 131 | "$(cc)" $(cc.gdjn.comp) -c -xc - -o "$@" 116 132 117 -$o/%.o: $s/%.c $s/%.h $(realpath $(janet.root)/include/janet.h) 133 +$o/%.o: $s/%.c $s/%.h $(janet.root)/include/janet.h 118 134 "$(cc)" -c $(cc.gdjn.comp) "$<" -o"$@" 119 135 120 -$o/rsrc.o: $t/rsrc.janet $(realpath $(janet)) \ 136 +$o/rsrc.o: $t/rsrc.janet $(janet) \ 121 137 $g/api.jimage 122 138 "$(janet)" "$<" -- "$g/api.jimage" 123 139 124 -%.jimage: $(realpath $(janet)) 140 +%.jimage: $(janet) 125 141 126 -$g/api.jimage: $t/api-compile.janet $(gd_api_spec) 127 - "$(janet)" "$<" "$(gd_api_spec)" "$@" 128 - 129 -$g/%.jimage: $l/%.janet 130 - "$(janet)" -c "$<" "$@" 142 +$g/api.jimage: $t/api-compile.janet $(wildcard $l/*.janet) $(janet) 143 + "$(janet)" "$<" "$@" 131 144 132 145 $x/janet $x/janet/src/include/janet.h: 133 146 "$(git)" $(git.flags) clone $(git.flags.clone) "$(janet.src.git)" "$x/janet" 134 147 135 148 canon = $(realpath $(dir $1))/$(notdir $1) 136 149 define janet.build = 137 150 "$(MAKE)" -C "$(janet.src.path)" "$(call canon,$@)" \ 138 151 JANET_$1="$(call canon,$@)" \ 152 + CFLAGS+="-O2 -g $(janet.cc) $(janet.cc.$2)" \ 139 153 $(janet.cfg) 140 154 endef 141 155 142 156 $o/libjanet.a: $(janet.src.path) 143 - $(call janet.build,STATIC_LIBRARY) 157 + $(call janet.build,STATIC_LIBRARY,lib) 144 158 $o/janet: $(janet.src.path) 145 - $(call janet.build,TARGET) 159 + $(call janet.build,TARGET,bin) 146 160 147 161 $g/extension_api.json $g/gdextension_interface.h: 148 162 cd "$g" && $(godot.cmd) --dump-extension-api-with-docs \ 149 163 --dump-gdextension-interface 150 164 151 165 152 166 # individual dependencies 153 167 154 168 janet-header = $(janet.root)/include/janet.h 155 169 156 -$o/gdjn.o: $s/util.h $g/interface.h $g/janet-lang.h $g/janet-rsrc.h $(gd_api_iface) 157 -$o/janet-lang.o: $s/util.h $(janet-header) 158 -$o/janet-rsrc.o: $s/util.h $g/janet-lang.h $(janet-header) 170 +$o/gdjn.o: $s/util-gd.h $s/type.h $g/interface.h $g/janet-lang.h $g/janet-rsrc.h $(gd_api_iface) 171 +$o/janet-lang.o $g/janet-lang.h: $s/util-gd.h $s/type.h $s/util-jn.h $(janet-header) 172 +$o/janet-rsrc.o $g/janet-rsrc.h: $s/util-gd.h $s/type.h $s/util-jn.h $g/janet-lang.h $(janet-header) 173 +$o/vm.o: $s/util-jn.h
Modified src/gdjn.c from [f8bd0edc4d] to [65eb39af07].
3 3 * 🄯 AGPLv3 4 4 * ? gdjn entry point 5 5 */ 6 6 7 7 #include "gdjn.h" 8 8 #include "janet-lang.h" 9 9 #include "janet-rsrc.h" 10 +#include "vm.h" 10 11 11 12 gdjn* gdjn_ctx = nullptr; 13 + 14 +void* gdjn_janet_malloc (size_t sz) { return _alloc(char, sz); } 15 +void gdjn_janet_free (void* ptr) { _free(ptr); } 16 +void* gdjn_janet_realloc(void* ptr, size_t sz) 17 + { return _realloc(ptr, char, sz); } 18 +void* gdjn_janet_calloc (size_t n, size_t esz) { 19 + const size_t sz = esz*n; 20 + void* v = _alloc(char, sz); 21 + memset(v, 0, sz); 22 + return v; 23 +} 12 24 13 25 static void 14 26 gdjn_init 15 27 ( void* data, 16 28 GDExtensionInitializationLevel lvl 17 29 ) { 18 30 if (lvl != GDEXTENSION_INITIALIZATION_SCENE) return; 19 31 gdjn_types_fetch(&gdjn_ctx -> gd.t, gdjn_ctx -> gd.getProc); 20 32 21 33 const gdjn_typeDB* c = &gdjn_ctx -> gd.t; 22 34 35 + janet_init(); 36 + gdjn_ctx -> jn.api = gdjn_vm_api_build_core(); 37 + janet_gcroot(janet_wrap_table(gdjn_ctx -> jn.api)); 38 + 23 39 gdjn_unit_janetLang_load(); 24 40 gdjn_unit_janetRsrc_load(); 25 41 26 42 gdjn_ctx -> gd.janetLang_inst = gdjn_class_JanetLang_new() -> self; 27 43 28 44 auto e = gd_engine_registerScriptLanguage( 29 45 c -> objects.engine, ................................................................................ 46 62 false 47 63 ); 48 64 gd_resourceSaver_addResourceFormatSaver( 49 65 gdjn_ctx -> gd.t.objects.resourceSaver, 50 66 gdjn_ctx -> gd.janetSaver_inst, 51 67 false 52 68 ); 53 - /* 54 - gd_variant ret; 55 - c -> gd_object.methodBindPtrcall( 56 - c -> gd_m_engine.registerScriptLanguage_ptr, 57 - c -> objects.engine, 58 - (GDExtensionConstTypePtr[]) { 59 - &gdjn_ctx -> gd.janetLang_inst, 60 - }, &ret 61 - ); 62 - */ 63 69 64 70 _t(array).empty(&gdjn_ctx -> gd.dox, nullptr); 65 71 gd_stringName empty = {}; 66 72 _t(stringName).empty(&empty, nullptr); 67 73 _t(array).setTyped(&gdjn_ctx -> gd.dox, 68 74 GDEXTENSION_VARIANT_TYPE_DICTIONARY, &empty, &empty); 69 75 ................................................................................ 75 81 ( void* data, 76 82 GDExtensionInitializationLevel lvl 77 83 ) { 78 84 if (lvl != GDEXTENSION_INITIALIZATION_SCENE) return; 79 85 /* we get double frees otherwise */ 80 86 81 87 const gdjn_typeDB* c = &gdjn_ctx -> gd.t; 88 + janet_gcunroot(janet_wrap_table(gdjn_ctx -> jn.api)); 82 89 83 90 gd_engine_unregisterScriptLanguage( 84 91 c -> objects.engine, 85 92 gdjn_ctx -> gd.janetLang_inst 86 93 ); 87 94 /* 88 95 GDExtensionTypePtr ret; ................................................................................ 99 106 gd_resourceSaver_removeResourceFormatSaver( 100 107 gdjn_ctx -> gd.t.objects.resourceSaver, 101 108 gdjn_ctx -> gd.janetSaver_inst 102 109 ); 103 110 /* gd_refCounted_unreference(gdjn_ctx -> gd.janetLoader_inst); */ 104 111 /* gd_refCounted_unreference(gdjn_ctx -> gd.janetSaver_inst); */ 105 112 gdjn_class_JanetLang_del(nullptr, gdjn_ctx -> gd.janetLang_inst); 113 + 114 + janet_deinit(); 106 115 107 116 gdjn_ctx -> gd.free(gdjn_ctx); 108 117 gdjn_ctx = nullptr; 109 118 } 110 119 111 120 112 121 gdBool
Modified src/gdjn.h from [b22d9ec405] to [a8262d25e1].
17 17 #define _warn(msg) _emit(warn, msg) 18 18 #define _err(msg) _emit(err, msg) 19 19 20 20 typedef GDExtensionBool gdBool; 21 21 22 22 #define _alloc(ty, n) \ 23 23 ((typeof(ty)*)gdjn_alloc(sizeof(ty) * (n))) 24 +#define _realloc(v, ty, n) \ 25 + ((typeof(ty)*)gdjn_realloc((v), sizeof(ty) * (n))) 24 26 #define _free(v) \ 25 27 (gdjn_ctx -> gd.free(v)) 26 28 #define _sz(r) ((sizeof(r) / sizeof(*r))) 27 29 28 30 #define _t(T) \ 29 31 (gdjn_ctx -> gd.t.gd_##T) 30 32 #define _method(name) \ ................................................................................ 60 62 61 63 GDExtensionObjectPtr 62 64 janetLang_inst, 63 65 janetLoader_inst, 64 66 janetSaver_inst; 65 67 } gd; 66 68 struct gdjn_jn { 67 - Janet env; 69 + JanetTable* api; 68 70 } jn; 69 71 } gdjn; 70 72 71 73 extern gdjn* gdjn_ctx; 72 74 73 75 [[gnu::alloc_size(1)]] static inline 74 76 void* gdjn_alloc(size_t sz) { 75 77 return gdjn_ctx -> gd.alloc(sz); 76 78 } 79 +[[gnu::alloc_size(2)]] static inline 80 +void* gdjn_realloc(void* v, size_t sz) { 81 + return gdjn_ctx -> gd.realloc(v, sz); 82 +} 77 83 78 84 79 85 typedef struct gdjn_gd gdjn_gd; // derp 80 86 typedef struct gdjn_jn gdjn_jn; 81 87 82 88 typedef struct gdjn_class_def gdjn_class_def; 83 89 84 90 void 85 91 gdjn_dox 86 92 ( gd_dictionary* page 87 93 );
Modified src/janet-lang.gcd from [c1b1ca0dcf] to [101fec1406].
1 1 (* [ʞ] src/janet-lang.gcd vi:ft=d 2 2 * ~ lexi hale <lexi@hale.su> 3 3 * 🄯 AGPLv3 4 4 * ? implement the godot-janet interface 5 5 *) 6 6 7 -use <janet.h>; 7 +import "type.h"; 8 +import "util-gd.h"; 9 +use "util-jn.h"; 10 +use <janet.h>; 8 11 9 12 class JanetLang is ScriptLanguageExtension { 10 - use <stdio.h>; 11 - use "util.h"; 13 + (* use <stdio.h>; *) 12 14 13 15 new {}; 16 + 17 + (* var as array[ref Object]: placeholders; *) 14 18 15 19 impl _get_name() -> string { 16 20 gd_string j; 17 21 _t(string).newWithUtf8Chars(&j, "Janet"); 18 22 return j; 19 23 }; 20 24 ................................................................................ 87 91 l("for"), l("forv"), l("forever"), 88 92 l("seq"), l("catseq"), 89 93 l("map"), l("mapcat"), 90 94 l("find"), 91 95 l("array"), l("tuple"), 92 96 l("string"), l("buffer"), 93 97 l("table"), l("struct"), 98 + l("print"), l("prin"), l("pp"), 99 + (* gdjn macros *) 100 + l("defm"), l("defm-"), 101 + l("class"), l("defclass"), l("defclass-"), 102 + l("prop"), 94 103 (* et cetera ad nauseam *) 95 104 }; 96 105 gd_packedStringArray r = {}; 97 106 _t(packedStringArray).empty(&r, nullptr); 98 107 for (size_t i = 0; i < _sz(words); ++i) { 99 108 gdu_array_string_pushPtr(&r, words[i].w, words[i].sz); 100 109 } ................................................................................ 118 127 return janscr -> self; 119 128 }; 120 129 impl _create_script() -> ref Object { 121 130 auto janscr = gdjn_class_JanetScriptText_new(); 122 131 return janscr -> self; 123 132 }; 124 133 134 + use { 135 + #define _typeCode(x) gd_ScriptLanguageExtension_LookupResultType_lookupResult##x 136 + }; 125 137 impl _get_documentation() -> array[dictionary] { 126 138 gd_array a = {}; 127 139 _t(array).ref(&a, &gdjn_ctx -> gd.dox); 128 140 return gdjn_ctx -> gd.dox; 141 + }; 142 + impl _lookup_code 143 + ( string code; 144 + string symbol; 145 + string path; 146 + ref Object owner; 147 + ) -> dictionary { 148 + gd_dictionary d; 149 + _t(dictionary).empty(&d, nullptr); 150 + gd_variant v; 151 + 152 + v = gd_variant_of_int(gd_Error_failed); 153 + gdu_setKey(&d, _pstrLit("result"), &v); 154 + 155 + v = gd_variant_of_int(_typeCode(ScriptLocation)); 156 + gdu_setKey(&d, _pstrLit("type"), &v); 157 + return d; 129 158 }; 130 159 131 160 impl _init() { /* (* "deprecated" but i still have to impl it?? *) */ }; 132 161 impl _frame() {}; 133 162 impl _thread_enter() { janet_init(); }; 134 163 impl _thread_exit() { janet_deinit(); }; 135 164 impl _finish() {}; ................................................................................ 153 182 _t(dictionary).empty(&dict,nullptr); 154 183 return dict; 155 184 }; 156 185 }; 157 186 158 187 class JanetScript is ScriptExtension { 159 188 var as string: path; 189 + var JanetTable*: env; 190 + 160 191 new { 161 192 _t(string).empty(&me -> path, nullptr); 193 + me -> env = nullptr; 162 194 }; 163 195 del { 164 196 _t(string).dtor(&me -> path); 197 + if (me -> env) janet_gcunroot(janet_wrap_table(me -> env)); 198 + }; 199 + 200 + class BaseInstance is Object { 201 + var as ref JanetScript: script; 202 + del { 203 + gd_refCounted_unreference(me -> script); 204 + printf("del script instance\n"); 205 + }; 206 + }; 207 + class Instance extends JanetScript_BaseInstance { 208 + }; 209 + class Placeholder extends JanetScript_BaseInstance { 165 210 }; 166 211 167 - impl _create_instance(array[variant] argv, int argc, ref Object owner, bool refCounted, int error) -> ref Object { 168 - 212 + (* impl _create_instance(array[variant] argv, int argc, ref Object owner, bool refCounted, int error) -> ref Object { *) 213 + impl _can_instantiate() -> bool { 214 + if (me -> env == nullptr) return false; 215 + return true; // FIXME 216 + }; 217 + impl _instance_create(ref Object obj) -> ref Object { 218 + auto ci = gdjn_class_JanetScript_Instance_new(); 219 + printf("made new script instance\n"); 220 + ci -> super.script = me; 221 + gd_refCounted_reference(me -> self); 222 + return ci; 223 + }; 224 + impl _placeholder_instance_create(ref Object obj) -> ref Object { 225 + auto ci = gdjn_class_JanetScript_Placeholder_new(); 226 + printf("made new script placeholder\n"); 227 + ci -> super.script = me; 228 + gd_refCounted_reference(me -> self); 229 + return ci; 169 230 }; 170 231 impl _get_language() -> ref ScriptLanguage { 171 232 return gdjn_ctx -> gd.janetLang_inst; 172 233 }; 173 234 impl _set_path(string path, bool takeOver) { 174 - if (takeOver) { 175 - _t(string).dtor(&me -> path); 176 - me -> path = path; 177 - } else { 178 - _t(string).dtor(&me -> path); 179 - /* _t(string).copy(&me -> path, (void const*[]) {&path}); */ 180 - me -> path = gdu_string_dup(&path); 181 - } 235 + _t(string).dtor(&me -> path); 236 + /* _t(string).copy(&me -> path, (void const*[]) {&path}); */ 237 + me -> path = gdu_string_dup(&path); 182 238 }; 183 239 impl _get_base_script() -> ref Script { 184 240 return nullptr; 185 241 }; 186 242 impl _has_static_method(string-name method) -> bool { 187 243 return false; 188 244 }; ................................................................................ 190 246 impl _get_global_name(string-name id) -> string-name { 191 247 return gdu_sym_null(); 192 248 }; 193 249 impl _is_abstract() -> bool { 194 250 return false; (* TODO abuse for non-class scripts? *) 195 251 }; 196 252 impl _get_instance_base_type() -> string-name { 197 - return _gdu_intern("Object"); 253 + return _gdu_intern("ScriptExtension"); 198 254 }; 199 255 impl _is_valid() -> bool { 200 256 return true; 201 257 }; 202 258 impl _get_documentation() -> array[dictionary] { 203 259 gd_array dox; 204 260 _t(array).empty(&dox, nullptr); 205 261 auto empty = gdu_sym_null(); 206 262 _t(array).setTyped(&dox, 207 263 GDEXTENSION_VARIANT_TYPE_DICTIONARY, &empty, &empty); 208 264 _t(stringName).dtor(&empty); 209 265 return dox; 210 266 }; 267 + 268 + (* 269 + impl _update_exports() { 270 + 271 + }; 272 + impl _placeholder_instance_create 273 + (ref Object forObj) -> ref Object { 274 + 275 + }; 276 + impl _placeholder_erased 277 + (ref Object ph) { 278 + }; 279 + *) 280 + 211 281 }; 212 282 213 283 class JanetScriptText extends JanetScript { 214 - var as string: src; 284 + var pstr: src; 215 285 new { 216 - _t(string).empty(&me -> src, nullptr); 286 + me -> src = (pstr){}; 217 287 }; 218 288 del { 219 - _t(string).dtor(&me -> src); 289 + _drop(me -> src); 220 290 }; 221 291 222 292 impl _has_source_code() -> bool { return true; }; 223 293 impl _get_source_code() -> string { 224 - auto d = gdu_string_dup(&me -> src); 225 - return d; 294 + return gdu_str_sz(me -> src.v, me -> src.sz); 295 + /* auto d = gdu_string_dup(&me -> src); */ 296 + /* return d; */ 226 297 }; 227 298 impl _set_source_code(string s) { 228 - _t(string).dtor(&me -> src); 229 - _t(string).copy(&me -> src, (void const*[]) {&s}); 299 + _drop(me -> src); 300 + me -> src = gdu_string_pdup(&s); 301 + /* printf("janet: set script %d %s\n", (int)me -> src.sz, me -> src.v); */ 302 + gdjn_class_JanetScriptText_method__reload(me, false); 303 + /* _t(string).dtor(&me -> src); */ 304 + /* _t(string).copy(&me -> src, (void const*[]) {&s}); */ 230 305 }; 231 306 impl _reload(bool keepState) -> int (* Error *) { 232 - (* TODO *) 233 - return gd_Error_ok; 307 + auto errorCode = gd_Error_ok; 308 + if (me -> super.env) 309 + janet_gcunroot(janet_wrap_table(me -> super.env)); 310 + me -> super.env = nullptr; 311 + pstr path = _gdu_string_stackp(&me -> super.path); 312 + 313 + auto cls = jnu_table_extend(gdjn_ctx -> jn.api, 8); 314 + janet_gcroot(janet_wrap_table(cls)); 315 + /* printf("janet: doing bytes %d %s\n", (int)me -> src.sz, me -> src.v); */ 316 + int e = janet_dobytes(cls, 317 + (uint8_t const*)me -> src.v, me -> src.sz, 318 + path.v, nullptr); 319 + (* we discard the return value; the environment is what 320 + * we're really interested in here *) 321 + /* printf("janet: bytes done, got %d\n",e); */ 322 + if (e == 0) { 323 + me -> super.env = cls; 324 + } else { 325 + _err("janet module could not be loaded"); 326 + errorCode = gd_Error_errParseError; 327 + janet_gcunroot(janet_wrap_table(cls)); 328 + } 329 + 330 + return errorCode; 234 331 }; 235 332 }; 236 333 237 334 import { 238 335 typedef struct gdjn_janet_image { 239 336 size_t sz; 240 337 uint8_t* buf;
Modified src/janet-rsrc.gcd from [ab1704aae3] to [2d0266f6b4].
1 1 (* [ʞ] src/janet-rsrc.gcd vi:ft=d 2 2 * ~ lexi hale <lexi@hale.su> 3 3 * 🄯 AGPLv3 4 4 * ? implement the saving and loading of janet scripts 5 5 *) 6 -use "util.h"; 6 +use "util-gd.h"; 7 7 use <assert.h>; 8 8 use "janet-lang.h"; 9 9 10 10 use { 11 11 static gd_packedStringArray 12 12 janetExts(void) { 13 13 gd_packedStringArray r = {}; ................................................................................ 40 40 || gdu_symEq(&type, "JanetScriptImage"); 41 41 }; 42 42 impl _get_resource_type(string path) -> string { 43 43 const char* str = ""; 44 44 switch (janetKind(&path)) { 45 45 case janetFileImage: str="JanetScriptImage"; break; 46 46 case janetFileText: str="JanetScriptText"; break; 47 + default: break; 47 48 } 48 49 return gdu_str(str); 49 50 }; 50 51 use { 51 52 static inline gd_variant 52 53 vFromErr(int64_t err) { 53 54 gd_variant v; ................................................................................ 65 66 }; 66 67 impl _load 67 68 ( string path; 68 69 string origPath; 69 70 bool subThreads; 70 71 int cacheMode; 71 72 ) -> variant { 73 + (* yes it's a bit hinky using hardwired static dispatch here 74 + * but ye gods, at least it spares us from having to use the 75 + * horrible gdscript ptrcall mechanism *) 76 + GDExtensionObjectPtr obj = nullptr; 77 + auto cpath = _gdu_string_stackp(&path); 78 + gd_string path_mine = gdu_str_sz(cpath.v, cpath.sz); 79 + auto fd = gd_fileAccess_open(path_mine, 80 + gd_FileAccess_ModeFlags_read); 81 + // auto cpath = _gdu_string_stackp(&path_mine); 82 + printf("janet: loading from file %zu %s\n", cpath.sz, cpath.v); 83 + /* gd_refCounted_reference(fd); */ 72 84 switch (janetKind(&path)) { 73 85 case janetFileImage: { 74 86 auto s = gdjn_class_JanetScriptImage_new(); 75 - return vFromObj(gdu_cast(s->self, "Resource")); 87 + gdjn_class_JanetScript_method__set_path(&s->super, path_mine, false); 88 + gdjn_class_JanetScriptImage_method__reload(s, false); 89 + obj = s -> self; 90 + break; 76 91 }; 77 92 case janetFileText: { 78 93 auto s = gdjn_class_JanetScriptText_new(); 79 - return vFromObj(gdu_cast(s->self, "Resource")); 80 - }; 81 - default: { 82 - return vFromErr(gd_Error_errFileUnrecognized); 94 + gdjn_class_JanetScript_method__set_path(&s->super, path_mine, false); 95 + 96 + auto text = gd_fileAccess_getAsText(fd, false); 97 + gdjn_class_JanetScriptText_method__set_source_code 98 + (s, text); 99 + _t(string).dtor(&text); 100 + obj = s -> self; 101 + break; 83 102 }; 103 + default: break; 84 104 } 105 + gd_fileAccess_close(fd); 106 + _t(string).dtor(&path_mine); 107 + /* gd_refCounted_unreference(fd); */ 108 + if (obj) return vFromObj(gdu_cast(obj, "Resource")); 109 + else return vFromErr(gd_Error_errFileUnrecognized); 85 110 }; 86 111 }; 87 112 88 113 89 114 class JanetScriptSaver is ResourceFormatSaver { 90 115 use { 91 116 static inline bool ................................................................................ 94 119 || _gdu_objIs(res, JanetScriptText); 95 120 } 96 121 }; 97 122 impl _get_recognized_extensions() -> packed-string-array { 98 123 return janetExts(); 99 124 }; 100 125 impl _recognize(ref Resource res) -> bool { 126 + printf("checking against res %p\n", res); 101 127 return gdjn_isJanet(res); 102 128 }; 103 129 impl _save(ref Resource res, string path, int flags) -> int { 104 130 gd_refCounted_reference(res); 105 131 assert(gdjn_isJanet(res)); 106 - gd_string path_mine; 107 - _t(string).copy(&path_mine, (void const*[]) {&path}); 132 + gd_string path_mine = gdu_string_dup(&path); 108 133 auto fd = gd_fileAccess_open(path, 109 134 gd_FileAccess_ModeFlags_write); 110 135 gd_refCounted_reference(fd); 111 136 112 137 if (_gdu_objIs(res, JanetScriptText)) { 113 138 auto asText = gdu_cast(res, "JanetScriptText"); 114 139 gd_string src = gd_script_getSourceCode(asText); 115 140 gd_fileAccess_storeString(fd, src); 116 141 _t(string).dtor(&src); 117 - auto data = gdjn_class_JanetScriptText_data(res); 118 142 } else if (_gdu_objIs(res, JanetScriptImage)) { 119 - auto asImg = gdu_cast(res, "JanetScriptImage"); 120 - auto data = gdjn_class_JanetScriptImage_data(res); 143 + // auto asImg = gdu_cast(res, "JanetScriptImage"); 144 + // auto data = gdjn_class_JanetScriptImage_data(res); 121 145 }; 122 146 123 - gd_fileAccess_close(fd); 147 + // gd_fileAccess_close(fd); 124 148 _t(string).dtor(&path_mine); 149 + printf("number of surviving saver refs: %zu\n", gd_refCounted_getReferenceCount(gdu_cast(me -> self, "RefCounted"))); 125 150 gd_refCounted_unreference(fd); 126 151 gd_refCounted_unreference(res); 152 + return gd_Error_ok; 127 153 }; 128 154 };
Added src/janetconf.h version [70cb9a69cc].
1 +#pragma once 2 + 3 +#ifdef _gdjn_shims 4 +// install shims for code compiled in library mode 5 +// yes this is awful, why do you ask 6 +void* gdjn_janet_malloc(size_t sz) { return malloc(sz); } 7 +void* gdjn_janet_realloc(void* ptr, size_t sz) { return realloc(ptr, sz); } 8 +void* gdjn_janet_calloc(size_t n, size_t sz) { return calloc(n, sz); } 9 +void gdjn_janet_free(void* ptr) { free(ptr); } 10 +#else 11 +# define janet_malloc gdjn_janet_malloc 12 +# define janet_calloc gdjn_janet_calloc 13 +# define janet_realloc gdjn_janet_realloc 14 +# define janet_free gdjn_janet_free 15 +#endif
Added src/type.h version [7c7116050b].
1 +/* [ʞ] src/type.h 2 + * ~ lexi hale <lexi@hale.su> 3 + * 🄯 AGPLv3 4 + * ? miscellaneous useful types & macros 5 + */ 6 + 7 +#pragma once 8 + 9 +#define _strDynSz(a) \ 10 + __builtin_choose_expr( \ 11 + /* if */ __builtin_types_compatible_p(typeof(a), pstr), \ 12 + /* then */ ((pstr*)&(a))->sz, \ 13 + /* else */ strlen(*(char const**)&(a))) 14 +#define _strDynMem(a) \ 15 + __builtin_choose_expr( \ 16 + /* if */ __builtin_types_compatible_p(typeof(a), pstr), \ 17 + /* then */ ((pstr*)&(a))->v, \ 18 + /* else */ *(char**)&(a)) 19 +#define _pstrOf(a) ((pstr){.v=_strDynMem(a), .sz=_strDynSz(a)}) 20 + 21 +#define _array(t) struct {t* v; size_t sz;} 22 +typedef _array(char) pstr; 23 + 24 +#define _pstrLit(a) ((pstr){.v=(a),.sz=sizeof(a)-1}) 25 + 26 +#define _drop(x) ({ \ 27 + if (x.v) { \ 28 + _free (x.v); \ 29 + x.v = nullptr; \ 30 + x.sz = 0; \ 31 + } \ 32 +}) 33 +#define _new(T, n) ((T){ \ 34 + .v = _alloc( typeof( ((typeof(T)) {}).v ), n), \ 35 + .sz = n, \ 36 +}) 37 +
Modified src/util-gd.h from [70ed0e36b0] to [79dbc22466].
1 -/* [ʞ] util.h 1 +/* [ʞ] src/util-gd.h 2 2 * ~ lexi hale <lexi@hale.su> 3 3 * 🄯 AGPLv3 4 - * ? encapsulate annoying operations (read: pitiful, fragile blast 5 - * shield over the most indefensibly psychotic pieces of the godot 6 - * "type" "system") 7 - * 8 - * if you want to use this outside gdjn, redefine the macro _t 9 - * from gdjn.h appropriately. 10 - * 11 - * (honestly tho you should use c-bind-gen.janet too) 4 + * ? encapsulate annoying godot operations (read: pitiful, 5 + * fragile blast shield over the most indefensibly psychotic 6 + * pieces of the godot "type" "system") 12 7 */ 13 8 14 9 #pragma once 15 10 #include "gdjn.h" 16 11 #include <string.h> 12 +#include "type.h" 17 13 18 14 static inline gd_string 19 15 gdu_string_of_stringName(const gd_stringName* const s) { 20 16 gd_string r; 21 17 _t(string).fromStringName(&r, (void*)&s); 22 18 return r; 23 19 } ................................................................................ 75 71 #define _refMut(x) typeof(typeof(x) * const) 76 72 #define _with(T, k, v, ...) ({\ 77 73 typeof(gd_##T) k = v; \ 78 74 do { __VA_ARGS__; } while (0); \ 79 75 _t(T).dtor(&k); \ 80 76 }) 81 77 82 -#define _withSym(k, v, ...) \ 83 - _with(stringName, k, gdu_intern(v), __VA_ARGS__) 84 78 #define _withSym0(k, ...) \ 85 79 _with(stringName, k, {}, __VA_ARGS__) 86 -#define _withStr(k, v, ...) \ 87 - _with(string, k, gdu_str(v), __VA_ARGS__) 88 -#define _withStr0(k, v, ...) \ 80 +#define _withSymSz(k, v, sz, ...) \ 81 + _with(stringName, k, gdu_intern_sz(v, sz), __VA_ARGS__) 82 +#define _withSym(k, v, ...) \ 83 + _withSymSz(k, _strDynMem(v), _strDynSz(v), __VA_ARGS__) 84 +#define _withSymLit(k, v, ...) \ 85 + _withSymSz(k, _litSz(v), __VA_ARGS__) 86 + 87 +#define _withStr0(k, ...) \ 89 88 _with(string, k, {}, __VA_ARGS__) 90 - 91 -#define _typeEq(a, b) \ 92 - __builtin_classify_type(typeof(a)) == _builtin_classify_type(typeof(b)) 93 - 94 -#define _refVal 0 95 -#define _refPtr 1 96 -#define _refArray 2 97 - 98 -#define _indirect(a) \ 99 - __builtin_types_compatible_p(typeof(a), void*) 100 - 101 -#define _refKind(a) \ 102 - __builtin_choose( _typeEq(typeof_unqual(a), \ 103 - typeof_unqual(a[0]) []), _refArray \ 104 - /* false */ __builtin_choose(_typeEq(a, (typeof_unqual(a[0]) *)), _refPtr )) 105 - 106 -#define _szElse(a, zero) \ 107 - __builtin_choose(_refKind(a) == _refArray, \ 108 - /* true */ _sz(a), \ 109 - /* false */ __builtin_choose(_refKind(a) == _refPtr, \ 110 - /* true */ (__builtin_counted_by(ptr) != nullptr ? \ 111 - *__builtin_counted_by(ptr) : (zero)) \ 112 - /* false */ (void)0 /* bad type */ )) 113 -#define _sz0(a) _szElse(a,0) 114 -#define _szStr(a) \ 115 - __builtin_choose(_typeEq((a), pstr), (a).sz, _szElse(a, strlen(a))) 116 - 117 -#define _array(t) struct {t* v; size_t sz;} 118 -typedef _array(char) pstr; 119 - 120 -#define _strWithSz(a) (a), _strLen(a) 89 +#define _withStrSz(k, v, sz, ...) \ 90 + _with(string, k, gdu_str_sz(v, sz), __VA_ARGS__) 91 +#define _withStr(k, v, ...) \ 92 + _withStrSz(k, _strDynMem(v), _strDynSz(v), __VA_ARGS__) 93 +#define _withStrLit(k, v, ...) \ 94 + _withStrSz(k, _litSz(v), __VA_ARGS__) 121 95 122 96 static inline bool 123 97 gdu_symIs 124 98 ( gd_stringName const* const a, 125 99 gd_stringName const* const b 126 100 ) { 127 101 bool res; ................................................................................ 225 199 char* buf = _alloc(char, len); 226 200 gdu_string_emit(s, buf, len - 1); 227 201 return (pstr){buf,len}; 228 202 } 229 203 230 204 static inline gd_string 231 205 gdu_string_dup (_ref(gd_string) s) { 232 - gd_string cp; 233 - _t(string).copy(&cp, (void const*[]) {s}); 234 - return cp; 206 + /* the godot copy method seems to be broken somehow, 207 + * probably for reasons that have to do with the 208 + * hypercomplicated CoW implementation that i think 209 + * is meant to be handled on this end somehow? we 210 + * can seemingly avoid crashes and memory corruption 211 + * if we copy the string through C, forcing a clean 212 + * new string to be generated on the godot side. */ 213 + // _t(string).copy(&cp, (void const*[]) {s}); 214 + auto cstr = gdu_string_pdup(s); 215 + auto copied = gdu_str_sz(cstr.v, cstr.sz); 216 + _drop(cstr); 217 + return copied; 235 218 } 236 219 237 220 #define _cat(x,y) x##y 238 221 #define __cat(x,y) _cat(x,y) 239 222 #define _gensym __cat(_sym_,__COUNTER__) 240 223 241 224 #define __gdu_string_auto(szid, name, str) \ ................................................................................ 371 354 static inline GDExtensionObjectPtr 372 355 gdu_cast 373 356 ( GDExtensionConstObjectPtr what, 374 357 _ref(char) to 375 358 ) { 376 359 return _t(object).castTo(what, gdu_classTag(to)); 377 360 } 361 + 362 +static inline void 363 +gdu_setKey 364 +( gd_dictionary* const dict, 365 + pstr const key, 366 + _ref(gd_variant) val 367 +) { 368 + gd_variant v = gd_variant_of_dictionary(*dict); 369 + _withSym(keyName, key, { 370 + uint8_t ok = false; 371 + _t(variant).setNamed(&v, &keyName, val, &ok); 372 + }); 373 +} 374 +
Added src/util-jn.h version [7073b831fd].
1 +/* [ʞ] util-gn.h 2 + * ~ lexi hale <lexi@hale.su> 3 + * 🄯 AGPLv3 4 + * ? convenience functions for janet 5 + */ 6 + 7 +#pragma once 8 +#include <janet.h> 9 + 10 +static inline void 11 +jnu_table_inherit(Janet tbl, Janet proto) { 12 + auto t = janet_unwrap_table(tbl); 13 + auto p = janet_unwrap_table(proto); 14 + t -> proto = p; 15 +} 16 + 17 +static inline JanetTable* 18 +jnu_table_extend(JanetTable* p, size_t sz) { 19 + auto t = janet_table(sz); 20 + t -> proto = p; 21 + return t; 22 +}
Added src/vm.c version [57a8603486].
1 +#include "vm.h" 2 +#include "util-jn.h" 3 +#include "util-gd.h" 4 +#include "rsrc.h" 5 + 6 +#define _safe_wrap(gdt, ct) ({ \ 7 + _Static_assert(sizeof(gdt) == sizeof(ct)); \ 8 + *(typeof(ct)*)v; \ 9 +}) 10 + 11 +void 12 +gdjn_dejanetize_typed 13 +( GDExtensionTypePtr v, 14 + GDExtensionVariantType const t, 15 + Janet val 16 +) { 17 + switch (t) { 18 + case GDEXTENSION_VARIANT_TYPE_BOOL: 19 + assert(janet_type(val) == JANET_BOOLEAN); 20 + *(bool*)v = janet_unwrap_boolean(val); 21 + break; 22 + case GDEXTENSION_VARIANT_TYPE_INT: 23 + switch (janet_type(val)) { 24 + case JANET_NUMBER: 25 + *(int64_t*)v = janet_unwrap_integer(val); break; 26 + case JANET_INT_S64: 27 + *(int64_t*)v = janet_unwrap_s64(val); break; 28 + case JANET_INT_U64: 29 + *(int64_t*)v = janet_unwrap_u64(val); break; 30 + default: assert(false); 31 + } 32 + break; 33 + case GDEXTENSION_VARIANT_TYPE_STRING: 34 + case GDEXTENSION_VARIANT_TYPE_STRING_NAME: { 35 + JanetString str; 36 + switch (janet_type(val)) { 37 + case JANET_STRING: str = janet_unwrap_string(val); break; 38 + case JANET_KEYWORD: str = janet_unwrap_keyword(val); break; 39 + case JANET_SYMBOL: str = janet_unwrap_symbol(val); break; 40 + default: assert(false); 41 + } 42 + size_t len = janet_string_length(str); 43 + if (t == GDEXTENSION_VARIANT_TYPE_STRING_NAME) { 44 + _t(stringName).newWithUtf8CharsAndLen(v, (char*)str, len); 45 + } else { 46 + _t(string).newWithUtf8CharsAndLen(v, (char*)str, len); 47 + } 48 + break; 49 + } 50 + case GDEXTENSION_VARIANT_TYPE_ARRAY: { 51 + } 52 + case GDEXTENSION_VARIANT_TYPE_DICTIONARY: { 53 + } 54 + default: { 55 + assert(false); 56 + } 57 + } 58 +} 59 + 60 +Janet 61 +gdjn_janetize_typed 62 +( GDExtensionTypePtr const v, 63 + GDExtensionVariantType const t 64 +) { 65 + switch (t) { 66 + case GDEXTENSION_VARIANT_TYPE_NIL: 67 + return janet_wrap_nil(); 68 + case GDEXTENSION_VARIANT_TYPE_BOOL: 69 + return janet_wrap_boolean(_safe_wrap(gd_bool, int8_t)); 70 + case GDEXTENSION_VARIANT_TYPE_INT: 71 + return janet_wrap_s64(_safe_wrap(gd_int, int64_t)); 72 + case GDEXTENSION_VARIANT_TYPE_FLOAT: 73 + _Static_assert( 74 + sizeof(gd_float) == sizeof(double) || 75 + sizeof(gd_float) == sizeof(float) 76 + ); 77 + return janet_wrap_number( 78 + (sizeof(gd_float) == sizeof(double)) ? *(double*)v : 79 + (sizeof(gd_float) == sizeof(float)) ? *(float*)v :0); 80 + 81 + case GDEXTENSION_VARIANT_TYPE_STRING: { 82 + auto str = gdu_string_pdup((gd_string*)v); 83 + auto j = janet_stringv((void*)str.v, str.sz); 84 + _free(str.v); 85 + return j; 86 + }; 87 + 88 + case GDEXTENSION_VARIANT_TYPE_STRING_NAME: { 89 + /* we can reasonably assume syms will be small enough 90 + * to fit on the stack and avoid a pointless malloc */ 91 + auto str = _gdu_stringName_stackp((gd_stringName*)v); 92 + auto j = janet_keywordv((void*)str.v, str.sz); 93 + return j; 94 + }; 95 + 96 + case GDEXTENSION_VARIANT_TYPE_ARRAY: { 97 + auto sz = gd_array_size(v); 98 + auto ja = janet_array(sz); 99 + for (size_t i = 0; i < sz; ++i) { 100 + auto val = _t(array).operatorIndexConst(v, i); 101 + auto j = gdjn_janetize(val); 102 + janet_array_push(ja, j); 103 + } 104 + return janet_wrap_array(ja); 105 + }; 106 + default: assert(false); 107 + } 108 +} 109 + 110 + 111 +typedef struct jn_closure { 112 + Janet (*fn)(void* data, int32_t argc, Janet* argv); 113 + void (*gc)(void* data); 114 + char alignas(max_align_t) data []; 115 +} jn_closure; 116 + 117 +typedef struct jn_hnd_dict { 118 + JanetAbstractHead header; 119 + GDExtensionVariantType key, val; 120 + gd_dictionary dict; 121 +} jn_hnd_dict; 122 + 123 +typedef struct jn_hnd_array { 124 + JanetAbstractHead header; 125 + GDExtensionVariantType ty; 126 + gd_array array; 127 +} jn_hnd_array; 128 + 129 +static int 130 +api_del_closure(void* ptr, size_t sz) { 131 + jn_closure* c = ptr; 132 + if (c -> gc != nullptr) { 133 + (*c -> gc)(c -> data); 134 + } 135 + return 0; 136 +} 137 + 138 +static Janet 139 +api_call_closure 140 +( void* ptr, 141 + int32_t argc, 142 + Janet* argv 143 +) { 144 + jn_closure* c = ptr; 145 + return (c -> fn)(c -> data, argc, argv); 146 +} 147 + 148 +static int 149 +api_del_dict(void* ptr, size_t sz) { 150 + jn_hnd_dict* dict = ptr; 151 + _t(dictionary).dtor(&dict -> dict); 152 + printf("drop dict\n"); 153 + return 0; 154 +} 155 +static int 156 +api_del_array(void* ptr, size_t sz) { 157 + jn_hnd_array* array = ptr; 158 + _t(array).dtor(&array -> array); 159 + printf("drop array\n"); 160 + return 0; 161 +} 162 + 163 +const JanetAbstractType jn_closure_def = { 164 + .name = "closure", 165 + .call = api_call_closure, 166 + .gc = api_del_closure, 167 +}; 168 + 169 +const JanetAbstractType jn_hnd_dict_def = { 170 + .name = "prim/type/dict", 171 + .gc = api_del_dict, 172 +}; 173 + 174 +const JanetAbstractType jn_hnd_array_def = { 175 + .name = "prim/type/array", 176 + .gc = api_del_array, 177 +}; 178 + 179 + 180 +static Janet 181 +api_new_array(int32_t argc, Janet* argv) { 182 + auto a = (jn_hnd_array*)janet_abstract(&jn_hnd_array_def, sizeof(jn_hnd_array)); 183 + _t(array).empty(&a -> array, nullptr); 184 + printf("create array\n"); 185 + return janet_wrap_abstract(a); 186 +} 187 + 188 +static Janet 189 +api_new_dict(int32_t argc, Janet* argv) { 190 + auto a = (jn_hnd_dict*)janet_abstract(&jn_hnd_dict_def, sizeof(jn_hnd_dict)); 191 + _t(dictionary).empty(&a -> dict, nullptr); 192 + printf("create dict\n"); 193 + return janet_wrap_abstract(a); 194 +} 195 + 196 + 197 +/* (prim/class-load [<ident> [<ident2>...]]) 198 + * low-level class loader. run at compile time to 199 + * import a godot class */ 200 +static Janet 201 +api_class_load(int32_t argc, Janet* argv) { 202 + return janet_wrap_nil(); /* FIXME */ 203 +} 204 + 205 +static const JanetReg reg_core [] = { 206 + {"class-load", api_class_load, 207 + "(prim/class-load ident)\n\n" 208 + "low-level loading function for Godot classes"}, 209 + {"type/array", api_new_array, 210 + "(prim/type/array [...])\n\n" 211 + "create a handle to a new godot array object"}, 212 + {"type/dict", api_new_dict, 213 + "(prim/type/dict <key-type> <val-type> {...})\n\n" 214 + "create a handle to a new godot dictionary object"}, 215 + {} 216 +}; 217 + 218 + 219 +JanetTable* 220 +gdjn_vm_api_spawnEnv (JanetTable* api) { 221 + /* create a clean new environment that can be used 222 + * and discarded by a script without contaminating 223 + * the global environment(s) 224 + * yes this is ooky */ 225 + auto env = jnu_table_extend(api, 8); 226 + auto sym_mc = janet_csymbolv("module/cache"); 227 + auto cleancache = jnu_table_extend( 228 + janet_unwrap_table(janet_table_get(api, sym_mc)), 8); 229 + 230 + janet_table_put(env, janet_csymbolv("module/cache"), janet_wrap_table(cleancache)); 231 + return env; 232 +} 233 + 234 + 235 +gdjn_vm_bind 236 +gdjn_vm_meta 237 +( JanetTable* bind 238 +) { 239 + gdjn_vm_bind b = {}; 240 + 241 + if (gdjn_vm_metaFlag(bind, "private")) goto fail; 242 + if (!gdjn_vm_metaKey(bind, "value", &b.val)) goto fail; 243 + 244 + if (gdjn_vm_metaFlag(bind, "macro")) 245 + b.kind = gdjn_vm_bind_mac; 246 + else { 247 + if (gdjn_vm_metaKey(bind, "method", &b.meta)) 248 + /* TODO assert callability */ 249 + b.kind = gdjn_vm_bind_method; 250 + else if (gdjn_vm_metaFlag(bind, "class")) 251 + b.kind = gdjn_vm_bind_class; 252 + else if (gdjn_vm_metaKey(bind, "type", &b.meta)) { 253 + if (gdjn_vm_metaFlag(bind, "prop")) { 254 + b.kind = gdjn_vm_bind_prop; 255 + } else /* constant */ { 256 + b.kind = gdjn_vm_bind_const; 257 + goto succeed; 258 + } 259 + if (gdjn_vm_metaFlag(bind, "share")) 260 + b.kind |= gdjn_vm_bind_flag_static; 261 + } else { 262 + switch (janet_type(b.val)) { 263 + case JANET_ABSTRACT: 264 + if ((janet_abstract_type(&b.val)) != &jn_closure_def) { 265 + b.kind = gdjn_vm_bind_const; break; 266 + } 267 + case JANET_FUNCTION: 268 + case JANET_CFUNCTION: 269 + b.kind = gdjn_vm_bind_method_static; 270 + break; 271 + default: goto fail; 272 + } 273 + } 274 + } 275 + /* found a valid export, return it */ 276 + succeed: return b; 277 + /* this binding is not marked correctly for gdexport */ 278 + fail: return (gdjn_vm_bind){gdjn_vm_bind_none}; 279 +} 280 + 281 +gdjn_vm_bind 282 +gdjn_vm_resv 283 +( JanetTable* env, 284 + Janet key 285 +) { 286 + auto gchnd = janet_gclock(); 287 + Janet def = janet_table_get(env, key); 288 + if (janet_type(def) == JANET_NIL) 289 + return (gdjn_vm_bind){}; 290 + auto m = gdjn_vm_meta(janet_unwrap_table(def)); 291 + janet_gcunlock(gchnd); 292 + return m; 293 +} 294 + 295 +void gdjn_vm_api_installCommon (JanetTable* tgt) { 296 + /* install primitives */ 297 + janet_cfuns(tgt, "prim", reg_core); 298 + auto idmap = janet_env_lookup(tgt); 299 + int gc = janet_gclock(); 300 + 301 + /* unpack API image */ 302 + Janet apiEnv = janet_unmarshal( 303 + gdjn_rsrc_api_jimage, 304 + sizeof gdjn_rsrc_api_jimage, 305 + 0, 306 + idmap, 307 + nullptr); 308 + printf("apienv type is %s\n", 309 + janet_type_names[janet_type(apiEnv)]); 310 + 311 + JanetTable* apiTbl = janet_unwrap_table(apiEnv); 312 + /* call the init function to precache base modules */ 313 + Janet initDef = janet_table_get(apiTbl, janet_csymbolv("init")); 314 + if (janet_type(initDef) == JANET_NIL) { 315 + _err("no init fn in api envtbl"); 316 + goto fail; 317 + } 318 + auto initFn = janet_unwrap_function( 319 + janet_table_get(janet_unwrap_table(initDef), 320 + janet_ckeywordv("value"))); 321 + Janet ret; 322 + auto e = janet_pcall(initFn, 0, nullptr, &ret, nullptr); 323 + if (e == JANET_SIGNAL_ERROR) { 324 + _err("failed to unpack the janet environment"); 325 + goto fail; 326 + } 327 + printf("environment load complete\n"); 328 +fail: 329 + janet_gcunlock(gc); 330 + /* janet_collect(); */ 331 +} 332 + 333 +JanetTable* gdjn_vm_api_build_compTime(void) { 334 + auto core = janet_core_env(nullptr); 335 + auto api = jnu_table_extend(core,32); 336 + gdjn_vm_api_installCommon(api); 337 + return api; 338 +} 339 + 340 +JanetTable* gdjn_vm_api_build_core(void) { 341 + auto core = janet_core_env(nullptr); 342 + auto api = jnu_table_extend(core,32); 343 + gdjn_vm_api_installCommon(api); 344 + return api; 345 +} 346 + 347 +JanetTable* 348 +gdjn_vm_compile 349 +( pstr const body, 350 + JanetTable* api, 351 + const char* ctx 352 +) { 353 + if (!ctx) ctx = "<anon>"; 354 + if (!api) api = gdjn_ctx -> jn.api; 355 + 356 + auto cls = jnu_table_extend(api, 8); 357 + janet_gcroot(janet_wrap_table(cls)); 358 + /* printf("janet: doing bytes %d %s\n", (int)me -> src.sz, me -> src.v); */ 359 + int e = janet_dobytes(cls, 360 + (uint8_t const*)body.v, body.sz, 361 + ctx, nullptr); 362 + /* we discard the return value; the environment is what 363 + * we're really interested in here */ 364 + /* printf("janet: bytes done, got %d\n",e); */ 365 + if (e != 0) { 366 + _err("janet module could not be loaded"); 367 + /* TODO capture parse error */ 368 + janet_gcunroot(janet_wrap_table(cls)); 369 + cls = nullptr; 370 + } 371 + return cls; 372 +} 373 + 374 + 375 +pstr 376 +gdjn_vm_image 377 +( JanetTable* env, 378 + JanetTable* binds 379 +) { 380 + return (pstr){}; //TODO 381 +}
Added src/vm.h version [dce8093e3b].
1 +#pragma once 2 +#include "gdjn.h" 3 +#include "type.h" 4 + 5 +Janet 6 +gdjn_janetize_typed 7 +( GDExtensionTypePtr const v, 8 + GDExtensionVariantType const t 9 +); 10 + 11 +static inline Janet 12 +gdjn_janetize(gd_variant* const v) { 13 + return gdjn_janetize_typed(v, _t(variant).getType(v)); 14 +} 15 + 16 +void 17 +gdjn_dejanetize_typed 18 +( GDExtensionTypePtr v, /* out */ 19 + GDExtensionVariantType const t, 20 + Janet val /* in */ 21 +); 22 + 23 +// typedef struct gdjn_vm_class { 24 +// 25 +// } gdjn_vm_class; 26 + 27 +JanetTable* gdjn_vm_api_build_compTime(void); 28 +JanetTable* gdjn_vm_api_build_core(void); 29 + 30 +JanetTable* 31 +gdjn_vm_compile 32 +( pstr const body, 33 + JanetTable* api, 34 + const char* ctx 35 +); 36 + 37 +pstr 38 +gdjn_vm_image 39 +( JanetTable* env, 40 + JanetTable* binds 41 +); 42 + 43 +typedef struct gdjn_vm_bind { 44 + enum gdjn_vm_bind_kind { 45 + gdjn_vm_bind_flag_static = 1 << 5, 46 + gdjn_vm_bind_flag_doc = 1 << 6, 47 + gdjn_vm_bind_flag_umask = ~( 48 + gdjn_vm_bind_flag_static | 49 + gdjn_vm_bind_flag_doc 50 + ), 51 + 52 + gdjn_vm_bind_none = 0, 53 + gdjn_vm_bind_const, 54 + gdjn_vm_bind_prop, 55 + gdjn_vm_bind_method, 56 + gdjn_vm_bind_class, 57 + gdjn_vm_bind_mac, /* always static unfortunately */ 58 + 59 + gdjn_vm_bind_prop_static = gdjn_vm_bind_prop 60 + | gdjn_vm_bind_flag_static, 61 + gdjn_vm_bind_method_static = gdjn_vm_bind_method 62 + | gdjn_vm_bind_flag_static, 63 + } kind; 64 + Janet val, meta; 65 +} gdjn_vm_bind; 66 + 67 +static inline bool 68 +gdjn_vm_metaKey 69 +( JanetTable* const bind, 70 + char const* const kw, 71 + Janet * valSlot 72 +) { 73 + Janet jv; 74 + if (valSlot == nullptr) valSlot = &jv; 75 + *valSlot = janet_table_get(bind, janet_ckeywordv(kw)); 76 + return (janet_type(*valSlot) != JANET_NIL); 77 +} 78 + 79 +static inline bool 80 +gdjn_vm_metaFlag 81 +( JanetTable* const bind, 82 + char const* const kw 83 +) { 84 + Janet v = janet_table_get(bind, janet_ckeywordv(kw)); 85 + return janet_truthy(v); 86 +} 87 + 88 +gdjn_vm_bind 89 +gdjn_vm_meta 90 +( JanetTable* bind 91 +); 92 + 93 +gdjn_vm_bind 94 +gdjn_vm_resv 95 +( JanetTable* env, 96 + Janet key 97 +);
Modified tool/api-compile.janet from [bd52030fd5] to [56067a22ef].
1 -(defn api-parse [src] 2 - {} #TODO parse json 3 - ) 1 +# [ʞ] tool/api-compile.janet 2 +# ~ lexi hale <lexi@hale.su> 3 +# 🄯 AGPLv3 4 +# ? gathers the core modules and assembles 5 +# them into a unified image that can be 6 +# loaded into gdjn. 7 + 8 +(def- extern-table @{}) 9 +(def marshal-map @{}) 10 +(def unmarshal-map @{}) 11 + 12 +(defn- safe-to-export? [x] 13 + # only ref types and explicit exportscan be safely replaced; 14 + # otherwise e.g. any use of the string "release" will be 15 + # replaced by the value of janet/build when loaded 16 + # yes this is lunacy i don't know why they did it like that 17 + (or (function? x) 18 + (cfunction? x) 19 + (table? x) 20 + (array? x) 21 + (buffer? x) 22 + (has-key? extern-table x))) 23 + 24 +(defn- register-env [env] 25 + (merge-into extern-table 26 + (tabseq [[sym data] :pairs env 27 + :when (has-key? data :value) 28 + :when (safe-to-export? (data :value))] 29 + (data :value) sym))) 30 + 31 + 32 +# provide symbol mapping for core values & 33 +# external symbols provided by gdjn 34 +(register-env root-env) 35 + 36 +(def modules '[ 37 + core prim json 38 +]) 39 + 40 +# here we iterate over the core modules and load each 41 +# in turn. the array *gd-api-map* is assembled at 42 +# compile-time and then used by the init function at 43 +# runtime to enumerate and install the core modules. 44 +# when core modules use a primitive, they declare their 45 +# own *gd-api-map* which maps gensyms to the name of 46 +# the desired primitive. 47 +(def *gd-api-map* @{}) 48 +(defn- install [name env] 49 + (put *gd-api-map* name env) 50 + (when (has-key? env '*gd-api-map*) 51 + (def gdmap (get-in env ['*gd-api-map* :value])) 52 + (loop [[val key] :pairs gdmap 53 + :when (safe-to-export? val)] 54 + (def sym (symbol '-val- (bxor (hash name) (hash val)))) 55 + (put marshal-map val sym) 56 + (put unmarshal-map sym val)) 57 + (merge-into extern-table gdmap))) 58 + 59 +(defn- log [fmt & r] 60 + (:write stderr "(api-compile) " 61 + (string/format fmt ;r))) 62 +(defn- install-mod [name] 63 + (log "loading library %s\n" name) 64 + (def env (require (string "/lib/" name))) 65 + (install (string name) env)) 66 + 67 +(each m modules (install-mod m)) 4 68 5 -(defn api-gen [api] 6 - @{} #TODO gen bindings 7 - ) 69 +# macro implementations can be provided directly in janet 70 +# and incorporated into the API image without having to 71 +# implement anything beyond the primitive API in C 8 72 9 -(defn main [_ api-src api-dest & _] 10 - (def api 11 - (with [fd (file/open api-src :r)] 12 - (api-gen (api-parse (:read fd :all))))) 13 - (def api-bin (make-image api)) 14 - (with [fd (file/open api-dest :w)] 15 - (:write fd api-bin)) 16 - 0) 73 +# this function is exported as part of api.jimage. 74 +# it will be called from vm.c after initializing 75 +# the primitive bindings 76 +(defn init [] 77 + (print "beginning merge") 78 + (merge-into module/cache *gd-api-map*) 79 + (print "api loaded")) 17 80 81 +# this is the build-time entry point, which does 82 +# the work of assembling the modules and marshalling 83 +# them out. it is not exported 84 +(defn main [_ out-path] 85 + (def env @{}) 86 + (each sym '[init marshal-map unmarshal-map] 87 + (put env sym ((curenv) sym))) 88 + (let [blob (marshal env extern-table)] 89 + (with [fd (file/open out-path :w)] 90 + (:write fd blob))) 91 + 0)
Modified tool/c-bind-gen.janet from [eeaa9e3ddf] to [37b502b2c6].
72 72 [ ;((<gd-sym> :%init) spec) 73 73 :binds (map bind-def 74 74 (or (spec :binds) [])) 75 75 :methods (map bind-def 76 76 (or (spec :methods) [])) 77 77 :ops (map bind-def 78 78 (or (spec :ops) [])) 79 + :indexed (or (spec :indexed) false) 79 80 :ctors (or (spec :ctors) {}) 80 - :mode (spec :mode)]) 81 + :mode (spec :mode) 82 + :c-repr (spec :c-repr)]) 81 83 :binds [] 82 84 :methods [] 83 85 :ctors {} 84 86 :ops [] 85 87 :mode : 88 + :c-type (fn [me] 89 + (string (or (me :c-repr) 90 + (string "gd_" (:name me))))) 86 91 :enum (fn [me] 87 92 (string "GDEXTENSION_VARIANT_TYPE_" (:scream me))) 88 93 )) 89 94 90 95 (defn env: [v dflt] 91 96 (or ((os/environ) v) dflt)) 92 97 ................................................................................ 121 126 (has-value? x))) 122 127 (def variants (map |(:@new <gd-type> $) ~[ 123 128 {:id variant :binds [get-ptr-constructor 124 129 get-ptr-destructor 125 130 get-ptr-operator-evaluator 126 131 get-ptr-internal-getter 127 132 get-ptr-builtin-method 133 + get-indexed set-indexed 134 + get-named set-named 128 135 get-type 129 136 booleanize]} 130 - {:id bool } 137 + {:id bool } 138 + {:id int } 139 + {:id float } 131 140 {:id color } 132 141 133 - ,;(map |{:id $} vector-types) 134 - ,;(map |{:id $ 142 + ,;(map |{:id $ :mode :dc} vector-types) 143 + ,;(map |{:id $ :mode :dc 135 144 :methods '[get set size resize fill clear 136 145 append append_array insert remove-at 137 146 has is-empty find rfind count 138 147 reverse slice duplicate] 139 148 :ctors {:empty []} 140 149 } 141 150 packed-types) 142 - {:id array 143 - :binds [ref set-typed] 144 - :ctors {:empty []}} 145 - {:id dictionary 151 + {:id array :mode :dc 152 + :binds [ref set-typed operator-index operator-index-const] 153 + :methods [size is-empty clear] 154 + :ctors {:empty []} 155 + :indexed true} 156 + {:id dictionary :mode :dc 146 157 :binds [set-typed operator-index operator-index-const] 147 - :ctors {:empty []}} 158 + :methods [size is-empty clear] 159 + :ctors {:empty []} 160 + :indexed true} 148 161 149 162 {:id string-name :mode :dc 150 163 :ops [equal] 151 164 :binds [new-with-utf8-chars 152 165 new-with-utf8-chars-and-len] 153 166 :methods [length 154 167 ends-with begins-with ................................................................................ 198 211 get-reference-count]} 199 212 {:id script :binds [get-source-code set-source-code]} 200 213 {:id file-access :binds [open close store-string get-as-text]} 201 214 {:id resource-loader :binds [add-resource-format-loader 202 215 remove-resource-format-loader]} 203 216 {:id resource-saver :binds [add-resource-format-saver 204 217 remove-resource-format-saver]} 218 + {:id script-language-extension} 219 + {:id script-extension} 205 220 ])) 206 221 207 222 (def global-enums (map |(:from <gd-sym> $) '[ 208 223 error 209 224 ])) 210 225 211 226 (def singletons (map |(:from <gd-sym> $) '[ ................................................................................ 224 239 (def api { 225 240 :decls @[] 226 241 :aliases @[] 227 242 :calls @[] 228 243 :defer-calls @[] # dependency Hel bypass 229 244 :types @[] 230 245 :method-defs @[] 246 + :methods-inline @[] 247 + :wrappers @[] 231 248 }) 232 249 (def config (env: "gd_config" "double_64")) 233 250 (def sizes (do 234 251 (var sz-list nil) 235 252 (loop [cfg :in (api-spec "builtin_class_sizes") 236 253 :until (not= nil sz-list)] 237 254 (when (= config (cfg "build_configuration")) ................................................................................ 246 263 (case (sizes x) 247 264 4 small 248 265 8 big 249 266 (error (string "bad type size " (sizes x) " for " x)))) 250 267 (case x 251 268 "int" (bp "int" "int32_t" "int64_t") 252 269 "float" (bp "float" "float" "double") 253 - "bool" "bool")) 270 + "bool" "bool" 271 + "object" "GDExtensionObjectPtr")) 254 272 (defn variant:gd->c [x] 255 273 (def v (find |(= x (:tall (:@new <gd-sym> {:id ($ :id)}))) variants)) 256 274 (string "gd_" (:name v))) 257 275 (defn translate-type [st &opt flags] 258 276 (defn fl [x] (string/check-set (or flags :) x)) 259 277 (match (string/split "::" st) 260 278 # enums can be directly mapped to a C ................................................................................ 342 360 (def id (string "gd_" (:tall class) "_" (e "name"))) 343 361 # the underlying type is IMPORTANT! godot enums 344 362 # appear to use the godot int type, which (at present) 345 363 # is always 8 bytes long. this means trying to write 346 364 # a godot "enum" to a plain old C enum will, if you 347 365 # are very lucky, cause your program to barf all over 348 366 # the stack and immediately segfault 349 - (ln "typedef enum %s : int64_t {" id) 367 + (ln "typedef enum %s : GDExtensionInt {" id) 350 368 # thank the gods for C23. this would have been really 351 369 # unpleasant otherwise 352 370 (each n (e "values") 353 371 (def ident (:from-snek <gd-sym> (n "name"))) 354 372 (def sym (:@new <gd-sym> ident)) 355 373 (ln "\t%s_%s = %d," id (:name sym) (n "value")) 356 374 (ln "\t/* %s */" ................................................................................ 399 417 (def name (:from-snek <gd-sym> (v "name"))) 400 418 (ln "\tgd_%s_%s = %d," (:tall e) (:name name) (v "value")) 401 419 (when (v "description") 402 420 (ln "\t/* %s */" (v "description")))) 403 421 (ln "} gd_%s;\n" (:tall e)) 404 422 ) 405 423 424 + (add (api :wrappers) 425 + (string 426 + "auto getWrap = " 427 + "(GDExtensionInterfaceGetVariantFromTypeConstructor)" 428 + `getProc("get_variant_from_type_constructor");` 429 + "\nauto getCast = " 430 + "(GDExtensionInterfaceGetVariantToTypeConstructor)" 431 + `getProc("get_variant_to_type_constructor");` "\n")) 406 432 (loop [v :in variants 407 433 :let [vsz (or (sizes (:tall v)) 408 434 (sizes (string (v :id) )))]] 409 435 (add (api :aliases) # these are all opaque objects 410 436 "typedef _opaque(%d) gd_%s;" 411 437 vsz (:name v)) 412 438 (add (api :decls ) "struct {") 413 439 (def my-enum (:enum v)) 414 440 (add-methods v (v :binds)) 415 441 (add-enums v) 442 + (unless (= (v :id) 'variant) 443 + (add (api :decls) 444 + "\tGDExtensionVariantFromTypeConstructorFunc wrap;") 445 + (add (api :decls) 446 + "\tGDExtensionTypeFromVariantConstructorFunc cast;") 447 + (add (api :wrappers) "t -> gd_%s.wrap = getWrap(%s);" 448 + (:name v) (:enum v)) 449 + (add (api :wrappers) "t -> gd_%s.cast = getCast(%s);" 450 + (:name v) (:enum v)) 451 + (def ct (or (prim:gd->c (:name v)) 452 + (variant:gd->c (:tall v)))) 453 + (add (api :methods-inline) 454 + (string "static inline %s\n" 455 + "gd_variant_to_%s\n" 456 + "(\tgd_variant const* const v\n" 457 + ") {\n" 458 + "\textern struct gdjn_typeDB* _g_typeDB;" 459 + "\t%s ret = {};\n" 460 + "\t_g_typeDB -> gd_%s.cast(&ret, (void*)v);\n" 461 + "\treturn ret;\n" 462 + "}\n") 463 + ct (:name v) 464 + ct (:name v) ) 465 + (add (api :methods-inline) 466 + (string "static inline gd_variant\n" 467 + "gd_variant_of_%s\n" 468 + "(\t%s v\n" 469 + ") {\n" 470 + "\textern struct gdjn_typeDB* _g_typeDB;" 471 + "\tgd_variant ret = {};\n" 472 + "\t_g_typeDB -> gd_%s.wrap(&ret, &v);\n" 473 + "\treturn ret;\n" 474 + "}\n") 475 + (:name v) 476 + ct 477 + (:name v) 478 + )) 416 479 417 480 # bind builtins 418 481 # WHY IS THIS *YET ANOTHER* COMPLETELY DIFFERENT API 419 482 # for the SAME LITERAL THING fuck youuuuu 420 483 (when (has-key? gdclasses (:tall v)) (loop [m :in (v :methods) 421 484 :let [method (get-in gdclasses [(:tall v) :methods 422 485 (:sulk m)])]] ................................................................................ 427 490 (translate-type (method :return_type)))) 428 491 (def args (method:args method (:tall v) 429 492 (if (method :is_const) :c :))) 430 493 431 494 (def impl @[]) 432 495 (unless (= return-type "void") 433 496 (array/push impl 434 - (string/format "typeof(%s) ret;" return-type))) 497 + (string/format "typeof(%s) ret = {};" return-type))) 435 498 (array/push impl 436 499 (string/format "_g_typeDB -> gd_%s.%s(" 437 500 (:name v) (:name m))) 438 501 439 502 440 503 (array/push impl 441 504 (string "\t" (if (method :is_static) ................................................................................ 619 682 (print "\ntypedef struct gdjn_typeDB {") 620 683 (loop [d :in (api :decls)] 621 684 (print "\t" d)) 622 685 (print "} gdjn_typeDB;") 623 686 (each t (api :types) (print t)) 624 687 (print c-fetch-decl ";") 625 688 (each m (api :method-defs) 626 - (print (m :dfn) ";"))) 689 + (print (m :dfn) ";")) 690 + (each m (api :methods-inline) 691 + (print m))) 627 692 628 693 "loader" (do 629 694 (print "#include <stdlib.h>\n" 630 695 "#include <stdio.h>\n" 631 696 "#include <assert.h>\n" 632 697 "#include \"interface.h\"\n\n" 633 - "static gdjn_typeDB* _g_typeDB;\n\n" 698 + "gdjn_typeDB* _g_typeDB;\n\n" 634 699 # HORRID HACK 635 700 636 701 c-fetch-decl "{\n" 637 702 ;(map |(string "\t" $ "\n") 638 703 [ "_g_typeDB = t;" 639 704 ;(api :calls) 705 + "{" 706 + ;(api :wrappers) 707 + "}" 640 708 ;(api :defer-calls) ]) 641 709 "}") 642 710 (each m (api :method-defs) 643 711 (print (m :dfn) "{\n\t" (string/join (m :impl) "\n\t") "\n}\n")))))
Modified tool/class-compile.janet from [a0ecf192d6] to [22543b9ce7].
33 33 (<- (any (+ (* `\` (backmatch :quo)) 34 34 (if-not (backmatch :quo) 1)))) 35 35 (backmatch :quo)) :quo))) 36 36 (defn fail [cause] 37 37 (defn mk [ln col] 38 38 {:kind :parse 39 39 :cause cause 40 + :src (dyn *src-file* "<stdin>") 40 41 :ln ln :col col}) 41 42 ~(error (cmt (* (line) (column)) ,mk))) 42 43 (defn req [id body] 43 44 ~(+ (* ,;body) ,(fail (keyword :malformed- id)))) 44 45 (defn kw [id body] 45 46 ~(* ,id (> 1 :bound) ,(req id ~(:s+ ,;body)))) 46 47 (defn comment-syntax [id open close] ................................................................................ 186 187 (enter body "")) 187 188 188 189 (defn err->msg [e] 189 190 (defn err-span [h & m] [:span [:hl [:red h]] ": " ;m ]) 190 191 (match e 191 192 {:kind :parse} (err-span "parse error" 192 193 (string (e :cause) " at ") 193 - [:hl (string/format "%d:%d" (e :ln) (e :col))]) 194 + [:hl (string/format "%s:%d:%d" (e :src) (e :ln) (e :col))]) 194 195 _ (error e))) 195 196 # _ (err-span "error" 196 197 # "something went wrong (" [:em (string e)] ")"))) 197 198 198 199 199 200 (defn indent [n lst] 200 201 (map |(string (string/repeat "\t" n) $) lst)) ................................................................................ 360 361 [:ref _] :object 361 362 x (keyword x))) 362 363 (defn gdtype->ctype [t] 363 364 (match t 364 365 :void :void 365 366 :float :double 366 367 :int :int64_t 367 - :bool :bool 368 + :bool :uint8_t 368 369 [:array _] :gd_array 369 370 [:dictionary _] :gd_dictionary 370 371 [:ref _] :GDExtensionObjectPtr 371 372 372 373 # use an opaque wrapper struct defined in interface.h 373 374 x (string "gd_" (:say (:read <sym> t))))) 374 375 ................................................................................ 377 378 ~(do (var ,acc ,init) 378 379 (loop ,binds 379 380 (set ,acc (do ,;body))) 380 381 ,acc)) 381 382 382 383 383 384 (defn unit-files [u] 384 - (def h @[ `#pragma once` `#include "gdjn.h"` `#include "util.h"` 385 + (def h @[ `#pragma once` `#include "gdjn.h"` 385 386 ;(u :header-prefix)]) 386 387 (def c @[ 387 388 (string `#include "` (:stab (u :name)) `.h"`) 388 389 `typedef struct gdjn_vcall {` 389 390 ` GDExtensionClassMethodPtrCall caller; ` 390 391 ` void* tgt;` 391 392 `} gdjn_vcall;` ................................................................................ 700 701 :method (array/push (cls :methods) meth) 701 702 :impl (array/push (cls :impls) meth))) 702 703 ))) 703 704 704 705 (def root (:new <cursor> :unit unit)) 705 706 (each n ast (process n root)) 706 707 707 - (defn class-prefix [c & r] 708 + (defn class-prefix* [sep c begin r] 708 709 (def pf (let [p (:prefix c)] 709 710 (if (empty? p) [] [p]))) 710 - (string/join ["gdjn_class" ;pf ;r] "_")) 711 + (string/join [;begin ;pf ;r] sep)) 712 + (defn class-prefix. [c & r] 713 + (class-prefix* "." c [] r)) 714 + (defn class-prefix_ [c & r] 715 + (class-prefix* "_" c ["gdjn_class"] r)) 711 716 712 717 (defn bind-methods [class] 713 718 (defn bind [f kind] 714 719 (def t-args (map (fn [[t] &] t) 715 720 (f :args))) 716 721 (def invocant 717 - ((unit :invocants) [(f :ret) t-args])) 722 + |((unit :invocants) [(f :ret) t-args])) 718 723 (def invocant-ptr 719 - ((unit :invocants-ptr) [(f :ret) t-args])) 724 + |((unit :invocants-ptr) [(f :ret) t-args])) 720 725 (def fp-t (func-ptr-t (gdtype->ctype (f :ret)) 721 726 [(string "typeof(" 722 - (class-prefix (class :cursor) 723 - (class :id))")*") 727 + (class-prefix_ (class :cursor) 728 + (class :id))")*") 724 729 ;(map gdtype->ctype t-args)])) 725 730 726 731 (def strings-lst @[]) 727 732 (def strings 728 733 (cache (fn [idx text] 729 734 (def id (string "_priv_str_" idx)) 730 735 (array/push strings-lst id text) ................................................................................ 763 768 `,`]) 764 769 :impl [`.return_value = ` 765 770 ;(prop-info (if (= :void (f :ret)) :nil 766 771 (f :ret)))] 767 772 )]) 768 773 ) 769 774 770 - (def fn-path (class-prefix (f :cursor) "method" (f :id))) 775 + (def fn-path (class-prefix_ (f :cursor) "method" (f :id))) 771 776 (with-names [:s_methodName (f :id) ;strings-lst] 772 777 (if (not= kind :method) "" 773 778 (string fp-t " func = " fn-path ";")) 774 779 (string/format `auto info = (%s) {` 775 780 (case kind 776 781 :method "GDExtensionClassMethodInfo" 777 782 :impl "GDExtensionClassVirtualMethodInfo")) ................................................................................ 780 785 (length t-args) ",") 781 786 ;(indent 1 arg-info) 782 787 ;(if (= kind :method) [ 783 788 ` .method_userdata = func,` 784 789 ` .method_flags = GDEXTENSION_METHOD_FLAGS_DEFAULT,` 785 790 (string "\t.has_return_value = " 786 791 (if (= :void (f :ret)) "false" "true") ",") 787 - (string "\t.call_func = " invocant ",") 788 - (string "\t.ptrcall_func = " invocant-ptr ",") 792 + (string "\t.call_func = " (invocant) ",") 793 + (string "\t.ptrcall_func = " (invocant-ptr) ",") 789 794 ] []) 790 795 ;ret-info 791 796 `};` 792 797 (comment (string `printf("binding method %s\n",` 793 798 (string/format "%q" fn-path) 794 799 `);`)) 795 800 (string `_t(classdb).` 796 801 (case kind 797 802 :method "registerExtensionClassMethod" 798 803 :impl "registerExtensionClassVirtualMethod") 799 804 `(gdjn_ctx -> gd.lib, &s_className, &info);`))) 800 - (array/concat @[] "{" ;(with-names [:s_className (class :id)] 805 + (def class-path (class-prefix* "_" (class :cursor) [] [(class :id)])) 806 + (array/concat @[] "{" ;(with-names [:s_className class-path] 801 807 ;(map |(bind $ :method) (class :methods)) 802 808 ;(map |(bind $ :impl) (class :impls))) "}")) 803 809 804 810 (defn push-item [kind cursor item] # abuse hashtables for fun & profit 805 811 (array/push (unit kind) item) 806 812 (when (and cursor (cursor :doc)) 807 813 (put (unit :doc) item (cursor :doc)))) 808 814 809 815 (loop [c :in (unit :classes)] 810 - (def id (class-prefix (c :cursor) (c :id))) 816 + (def id (class-prefix_ (c :cursor) (c :id))) 811 817 (def [id-ctor id-dtor 812 818 id-ctor-api 813 819 id-init id-create] 814 820 (map |(string id "_" $) ["new" "del" 815 821 "api_new" "init" "create"])) 816 822 817 823 (def id-base (as-> (c :base) b ................................................................................ 820 826 (string "gdjn_class_" b))) #HAAACK 821 827 (when (not (empty? (c :impls))) 822 828 (def vtbl @[]) 823 829 (loop [i :range [0 (length (c :impls))] 824 830 :let [f ((c :impls) i)]] 825 831 (def t-args (map (fn [[t] &] t) 826 832 (f :args))) 827 - (def call (class-prefix (f :cursor) "method" (f :id))) 833 + (def call (class-prefix_ (f :cursor) "method" (f :id))) 828 834 (def caller ((unit :invocants-ptr) [(f :ret) t-args])) 829 835 (put (c :vtbl-map) (f :id) i) 830 836 (array/push vtbl 831 837 (string "{.caller=" caller ", .tgt=" call "}")) 832 838 ) 833 839 (let [vstr (string/join (map |(string "\n\t" $ ",") vtbl)) 834 840 vwr (string "{" vstr "\n}") ] ................................................................................ 862 868 (with-names ["superName" (c :base)] 863 869 "super = _t(classdb).constructObject(&superName);") 864 870 [(string/format "super = %s_create();" 865 871 id-base)]) 866 872 "return super;")) 867 873 868 874 (def icb-null "(&(GDExtensionInstanceBindingCallbacks){})") 875 + (def class-path (class-prefix* "_" (c :cursor) [] [(c :id)])) 869 876 (array/push (unit :funcs) 870 877 (:dec <func-c> self-ref-t id-ctor [] 878 + (string `printf("creating object ` id `\n");`) 871 879 (string "typeof("id")* me = _alloc("id", 1);") 872 - ;(with-names ["className" (c :id)] 880 + ;(with-names ["className" class-path] 873 881 (string/format "auto gdobj = %s();" 874 882 id-create) 875 883 #`printf("constructed super object %p\n", gdobj);` 876 884 "_t(object).setInstance(gdobj, &className, me);" 877 885 # register the instance as a binding so 878 886 # that other classes can access it. this 879 887 # is so dumb ................................................................................ 884 892 (string id-init "(me, gdobj);")) 885 893 "return me;")) 886 894 (push-event :dtor id-dtor :void 887 895 [[:void* :_data] 888 896 [:GDExtensionClassInstancePtr :_ptr_me]] 889 897 |[(string "typeof("id")* me = _ptr_me;") 890 898 ;$ 891 - "_free(me);"]) 892 - (def id-virt (class-prefix (c :cursor) (c :id) "virt")) 899 + (if (= :native (c :base-mode)) "_free(me);" 900 + (string/format "%s_del(_data, &me -> super);" 901 + id-base))]) 902 + (def id-virt (class-prefix_ (c :cursor) (c :id) "virt")) 893 903 (array/push (unit :funcs) 894 904 (:dec* <func-c> [:inline :static] 895 905 self-ref-t (string id "_data") 896 906 [[:GDExtensionObjectPtr :self]] 897 907 "return _t(object).getInstanceBinding(" 898 908 " self, gdjn_ctx -> gd.lib," 899 909 icb-null 900 910 ");")) 901 911 902 912 (array/push (unit :funcs) (:dec- <func-c> :void* id-virt 903 913 [[:void* :data] 904 914 [:GDExtensionConstStringNamePtr :method] 905 915 [:uint32_t :hash]] 906 - `bool res = false;` 907 - ;(catseq [[name idx] :pairs (c :vtbl-map)] [ 908 - ;(with-names [:name name] 909 - `_t(stringName).equal(&name, method, &res);` 910 - `if (res) {` 911 - (string "\treturn (void*)&" id "_vtbl[" idx "];") 912 - `}`)]) 916 + ;(catseq [[name idx] :pairs (c :vtbl-map)] 917 + [ `{bool res = false;` 918 + ;(with-names [:name name] 919 + `_t(stringName).equal(&name, method, &res);`) 920 + `if (res) {` 921 + (string "\treturn (void*)&" id "_vtbl[" idx "];") 922 + `}}`]) 913 923 ;(if (= :native (c :base-mode)) [`return nullptr;`] 914 924 # inherits from a gdextension class; call up 915 925 [(string/format "return %s_virt(data, method, hash);" 916 926 id-base)]) 917 927 )) 918 - (def id-virt-call (class-prefix (c :cursor) (c :id) "virt_call")) 928 + (def id-virt-call (class-prefix_ (c :cursor) (c :id) "virt_call")) 919 929 (array/push (unit :funcs) (:dec- <func-c> :void id-virt-call 920 930 [[:GDExtensionClassInstancePtr :inst] 921 931 [:GDExtensionConstStringNamePtr :method] 922 932 [:void* :vcall] 923 933 ["const GDExtensionConstTypePtr*" :args] 924 934 [:GDExtensionTypePtr :ret]] 925 935 `auto c = (const gdjn_vcall*)vcall;` 926 936 `c -> caller(c -> tgt, inst, args, ret);`)) 937 + (def class-path (class-prefix* "_" (c :cursor) [] [(c :id)])) 927 938 (array/push (unit :load) 928 - ;(with-names ["className" (c :id) 939 + ;(with-names ["className" class-path 929 940 "superName" (c :base)] 930 941 "auto classDesc = (GDExtensionClassCreationInfo4) {" 931 942 ` .is_virtual = false,` 932 943 (string "\t.is_abstract = " (if (c :abstract) "true" "false")",") 933 944 ` .is_exposed = true,` 934 945 ` .is_runtime = true,` 935 946 (string "\t.create_instance_func = " id-ctor-api ",") ................................................................................ 956 967 (def binder (bind-methods c)) 957 968 (array/concat (unit :load) binder) 958 969 959 970 960 971 961 972 ) 962 973 (loop [f :in (unit :methods)] 963 - (def class (class-prefix (f :cursor))) 964 - (def cid (class-prefix (f :cursor) "method" (f :id))) 974 + (def class (class-prefix_ (f :cursor))) 975 + (def cid (class-prefix_ (f :cursor) "method" (f :id))) 965 976 (def cfn (:dec <func-c> (gdtype->ctype (f :ret)) cid 966 977 [ [(string class "*") "me"] 967 978 ;(map (fn [[t id dox]] 968 979 [(gdtype->ctype t) id]) 969 980 (f :args))] 970 981 (f :text))) 971 982 (def arg-dox ................................................................................ 997 1008 (let [uf (unit-files unit)] 998 1009 (:write stdout (case emit 999 1010 "header" (lines->str (uf :header)) 1000 1011 "loader" (lines->str (uf :impl)) 1001 1012 (error :bad-cmd))))) 1002 1013 1003 1014 (defn main [& argv] 1004 - # (entry ;argv)) 1005 - (try (entry ;argv) 1006 - ([e] (:write stderr (style ;(err->msg e)))))) 1015 + (entry ;argv)) 1016 + # (try (entry ;argv) 1017 + # ([e] (:write stderr (style ;(err->msg e)) "\n"))))
Modified tool/rsrc.janet from [5689af4629] to [c425f8a03e].
28 28 (let [fd (file/open path :r) 29 29 rec {:id (reduce |(string/replace-all $1 "_" $0) 30 30 (basename path) ["-" "." "/"]) 31 31 :vals (blob->c-array (:read fd :all))}] 32 32 (:close fd) 33 33 rec)) 34 34 35 +(defn c-base [t] 36 + (string "const uint8_t gdjn_rsrc_" (t :id) " [" (length (t :vals))"]")) 35 37 (defn c-decl [t] 36 - (string "const uint8_t gdjn_rsrc_" (t :id) " []")) 38 + (string "extern " (c-base t))) 37 39 (defn c-def [t] 38 - (string (c-decl t) " = {" (t :vals) "}")) 40 + (string (c-base t) " = {" (t :vals) "}")) 39 41 40 42 41 43 (defn c-compile [c to-path] 42 44 (def cc (os/spawn [(dyn *cc*) 43 45 "-xc" "-c" "-" 44 46 "-o" to-path] 45 47 :p {:in :pipe}))