* /ZRE/ (Zongors Reality Engine) Design parameters :PROPERTIES: :CUSTOM_ID: zre-zongors-transpiler-language-design-parameters :END: ** What is /zre/? :PROPERTIES: :CUSTOM_ID: what-is-zre :END: /zre/ is an domain specific language for 3d games with C/Lua style syntax. The compiler is written in C which should make it easy to port to other systems. * /ZRE/ Grammar and Specification :PROPERTIES: :CUSTOM_ID: zre-grammar-and-specification :END: ** Types :PROPERTIES: :CUSTOM_ID: types :END: - there are also a list of "substantial types" which come with the language which are the building blocks for more complex types. If you are coming from object oriented languages you can think of self as "primitive types" #+begin_src zre type «token» { init() { // values } } #+end_src zre * Basic Types :PROPERTIES: :CUSTOM_ID: substantial-types :END: ** numeric :PROPERTIES: :CUSTOM_ID: numeric :END: - =real= - 64 bit floating point (Double) by default for modern CPUs that have a FPU - 32 bit 16.16 for microcontrollers/hardware without a FPU like a raspberry pi pico, retro hardware like a Mac 68k - 16 bit 12.4 for older retro hardware like PS1 or UXN. - Is it slow and takes up a lot of space? yeah, kinda, maybe, the nice part of a unified numeric type is that makes it so that you do not have to worry about type casting which actually ends up speeding up processing This is also how Lua, Lox, and Wren programming language handles numbers. Also because ZRE is intended to be used for Games, floats are used more for 3D graphics than other numeric types. ** string :PROPERTIES: :CUSTOM_ID: string :END: - =str= - utf8 / ascii encoded string depending on the language output normal string ="«utf8 encoded characters»"= string interpolation ="«utf8 encoded characters» ${some_var}"= ** binary :PROPERTIES: :CUSTOM_ID: binary :END: - =byte= - same as uint8 or c char, also used for interop ** logical :PROPERTIES: :CUSTOM_ID: logical :END: - =bool= - =true= / =false= ** error :PROPERTIES: :CUSTOM_ID: error :END: error is a type which describes an error that occurred, it is similar to the Go programming language and is returned as a monad like the maybe monad above and is unwrapped in a similar way. You could also think of it as every variable being able to have "the type" and also "error" as a possible value. #+begin_src zre let rr = err("something borked"); let var = rr ?? 0; // value is 0 let other_var = rr ?? panic(rr); // will panic #+end_src zre ** datastructure :PROPERTIES: :CUSTOM_ID: datastructure :END: Much like Lua, zwl only has tables. Lua's tables are amazing and very unique. Why have five different datastructures to do things when you can just have one that does everything? Types that can be indexes are numbers and strings (no objects); *** table :PROPERTIES: :CUSTOM_ID: table :END: syntax (yes I was nice and kept the syntax the same as most C like langs) #+begin_src zre /* array same as a map of int to «type» */ let «variable» = [val1, val2, ...]; /* or as a map */ let «variable» = {key1: val1, key2: val2, ...}; #+end_src zre *** tunnel :PROPERTIES: :CUSTOM_ID: tunnel :END: described in "tunnel" section *** Basic operators :PROPERTIES: :CUSTOM_ID: basic-operators :END: The following is a list of global operators and their effect: - // - comment - ?? - unwrap or - .? - null check or return error - + - addition - - - subtraction - negation - * - multiplication - / - divisor - ^ - power - == - equals - < - less than - > - greater than - >= - greater than or equals - <= - less than or equals - . - accessor - ++ - inline add 1 - -- - inline subtract 1 - += - inline add n - -= - inline subtract n - *= - inline multiply n - \= - inline divide n *** logical / bitwise operators :PROPERTIES: :CUSTOM_ID: logical-bitwise-operators :END: - =mod= - modulo - =and= - logical and - =or= - logical or - =xor= - logical xor - =band= - bitwise and - =bor= - bitwise or - =bxor= - bitwise xor - =srl= - bit shift right - =sll= - bit shift left *** keywords :PROPERTIES: :CUSTOM_ID: keywords :END: =let= let operator #+begin_src zre let «token» = true; #+end_src zre =is= checks if a object is of that type #+begin_src zre if («token» is real) { print("hello yes self is a real?"); } #+end_src zre also used for letting constants =as= coerces a type as another type if possible #+begin_src zre let «token» = 0; /* default is real */ some_functon(«token» as byte); /* needs an byte */ #+end_src zre =in= checks if a object's type, or a type impls another type #+begin_src zre if («token» in Tunnel) { print("im tunnel-able"); } #+end_src zre also used inside of the for loops #+begin_src zre for («token» in «collection») { «body» } #+end_src zre ** Object :PROPERTIES: :CUSTOM_ID: object :END: An object is an invoked type. #+begin_src zre let «variable» = «type»(«fields», …); #+end_src zre ** Tunnel :PROPERTIES: :CUSTOM_ID: tunnel-1 :END: Represents a path to a file, url endpoint, other process endpoint (like a socket, etc.) Tunnels are inspired by translators in gnu/hurd, plan9 9p protocol, and unix sockets tunnels are invoked like objects, but have scope like control flow end scope closes the tunnel note the type must always be of a type which is "tunnel-able" i.e. Files, sockets, etc Tunnels have almost the same interface as 9p since they are closely based on 9p. *** transtypes for tunnels :PROPERTIES: :CUSTOM_ID: transtypes-for-tunnels :END: =tunnel? : attach(tunnel_object)= -> open communication =success? : tunnel_object.clunk()= -> close communication =success? : tunnel_object.flush()= -> cancels long operation and dumps whatever is in buffer =success? : tunnel_object.open(resource, mode)= -> opens a tunnel for doing operations on =success? : tunnel_object.create(resource)= -> creates the object from the database graph/file from file structure =data? : tunnel_object.read(resource)= -> reads from a tunnel =success? : tunnel_object.write(resource, data)= -> writes to a tunnel =success? : tunnel_object.remove(resource)= -> removes the object from the database graph/file from file structure =stat_data? : tunnel_object.stat(resource)= -> returns the status of the file/resource =version? : tunnel_object.version()= -> returns the version code for the connected tunnel =success? : tunnel_object.walk(path_or_endpoint)= -> moves around the filesystem or through the graph #+begin_src zre /* client */ let endpoint = `protocol://path/to/source`; let tunnel = endpoint.attach(user, auth); let data = tunnel.open("/some/resource").read(); std.write(data); //print(data); data.flush(); endpoint.clunk(); /* server */ let server = `protocol://ip`; s.bind("/some/resource", fn () str { return "hello world"; }) server.start(); #+end_src zre ** Functions :PROPERTIES: :CUSTOM_ID: functions :END: Functions are all typechecked statically at compile time. Since we always have a "default type" for all constant values or a developer can use the =as= keyword we do not have to define all values like in C, while keeping the same type safety as a more strongly typed language. #+begin_src zre fn «token» («parameter» «type», ...) «return_type» { «body» } #+end_src zre - Built in transtypes - sort - filter - trig functions - calc functions - statistical functions ** Control flow :PROPERTIES: :CUSTOM_ID: control-flow :END: *** loops :PROPERTIES: :CUSTOM_ID: loops :END: #+begin_src zre for («token» in «collection») { «body» } #+end_src zre iterates through each object in the collection setting it to token #+begin_src zre while («boolean expression») { «body» } #+end_src zre loops until the expression is false #+begin_src zre loop { «body» } #+end_src zre loops infinitely until break or return #+begin_src zre do («variable» = initial_value, end_value, increment) { «body» } #+end_src zre loops from initial value to end value by increment value *** branching :PROPERTIES: :CUSTOM_ID: branching :END: #+begin_src zre if («boolean expression») { } else if («boolean expression») { } else { } #+end_src zre *** exceptions :PROPERTIES: :CUSTOM_ID: exceptions :END: take a look at error's, but you can panic on an error like self: #+begin_src zre panic(err("error message")); panic(err(3)); panic(«some_error_token»); #+end_src zre ** Localization :PROPERTIES: :CUSTOM_ID: localization :END: will look up the text of «token» in the linked localization.json file #+begin_src zre #«token» #+end_src zre #+begin_src json { "some_token": [ "localization_1": "" ], "some_other_token": [ "localization_1": "", "localization_2": "" ] } #+end_src ** Libraries and "includes" :PROPERTIES: :CUSTOM_ID: libraries-and-includes :END: In most languages the include or use statements get libraries which link to other files and so on. Self quickly gets confusing and so requires package managers and installers, etc. The other way to do self would be to just specifically "name" the paths using a tunnel and import it. You can even use localization tokens to create config files. Since everything is lazily compiled jit anyways it (in theory) doesn't hurt pertypeance much #+begin_src zre use `https://example.com/some_library/some_file.zre` #+end_src zre #+begin_src zre use `./some_local_file.zre` #+end_src zre ** Testing :PROPERTIES: :CUSTOM_ID: testing :END: *** assertion :PROPERTIES: :CUSTOM_ID: assertion :END: #+begin_src zre assert(«expression», «expected output») /* returns «error or none» */ #+end_src zre ** Measurements :PROPERTIES: :CUSTOM_ID: measurements :END: - types - time - unit - seconds (s) - subtypes - date - Default is ISO 8601 - length - unit - metre (m) - subtypes - angle - radian (rad) - mass - unit - kilogram (kg) - electric current - unit - ampere (a) - temperature - unit - kelvin (K) - amount of substance - unit - mol (mol) - luminous intensity - unit - candela (candela)