Pixel art of a red deer, the Deor mascot

Deor

A language for all, transpiled to Rust, as simple as Python, as procedural as Fortran

Learn more by reading the Documentation

Getting Started is the place to go to get up and running.

Pi Calculator Sample

for readability
    
    # Imports must be declared at the top of a file but are global, this means
    #  imports can all be imported in main or from an imports.deor file imported by main
    #  this follows the old C-methodology you must be careful about naming root level items
    import "lib/convert.deor"
    import "lib/string.deor"
    import "lib/math.deor"
    # This may look strange, it is a substitution for all T replaces with PiChunk, the main
    #  use of this is setting types for the import, a way of passing a generic
    import "lib/tasks.deor" where T = PiChunk

    # structs are data, no methods can be attached to the data; Deor is strongly procedural
    struct PiChunk
    	int min
    	int max
    	float value

    # These are validator types that wrap Rust's Option, they return valid (or not valid) if they are 
    #  set to a value that matches the predicate, this is the only way to have a "null/undefined" in Deor
    type SmallPositive(int value)
    	value > 0 and value <= 32

    type LargePositive(int value)
    	value > 0 and value <= 100_000

    # Shapes are the equivalent to generics in other languages, Deor believes in strong human
    #  readability so we use shapes (not <>) for lists and generic function definitions. piChunkList
    #  here is actually redundant (the tasks import has this shape that is defined with T-substitution):
    #  which brings us to another important point: Deor will simply drop root level items that are clones
    shape piChunkList = func of PiChunk to PiChunk


    # Macros are ways you can insert code (as seen below with macro_run) as part of the transpiler,
    #  it is as if you had written it that way. Consts can't be global, so this is a useful pattern.
    macro use_print_consts
    	const string SEPARATOR = "): "
    	const string NONE = ""


    # Because Deor clones all items by default, iteration in loops clones arguments to functions
    #  (which is a problem for a list or large struct), while not true here, macros for organization 
    #  in for loops are not a bad option as many performance issues come from looping over function calls
    macro leibniz_iteration

    	# As will allow the compiler to determine the type for you and initialize that variable in one go
    	is_even as (iter % 2 is 0)
    	decimal_iter as c_int_to_float(iter)
    	denom as 1.0 + decimal_iter * 2

    	if is_even
    		value = value + 1.0 / denom
    	else
    		value = value - 1.0 / denom


    # Functions and variables are C style in definition {type} {name}
    fn PiChunk compute_pi_chunk(PiChunk chunk)

    	# There is no dot-notation in Deor, con/de(struction) is used to keep variable names consistent
    	(min, max) in chunk
    	# In addition to using as, you can also spell out types, this is recommended as a best practice
    	#  if the type of the value is not acutely obvious from what is being assigned to it
    	float value = perform_liebniz_computation(min, max)
    	# Here we are using a 'with' this clones the struct and replaces the matching field
    	PiChunk chunk_with_value = chunk with (value)

    	return chunk_with_value


    fn float perform_liebniz_computation(int start_iter, int end_iter)
    	float value = 0.0

    	# Loops use ranges like other languages such as Python these can be used in various ways, here it
    	#  is going from a start to end value. Note: end is reserved in Deor as a keyword and can't be used
    	for iter in range(start_iter, end_iter)
    		macro_run leibniz_iteration

    	return value


    # Although still well under the limit, functions have a strict limit of three parameters, if passing
    #  more, you must use a struct. Again: the focus here is human readability
    fn piChunkList build_chunk(int max_iterations, int iterations_per_thread)

    	# While a list can be assigned literally with [value1,value2] it can't be assigned a [], this
    	#  is because, once again, Deor focuses on readability. As a result, you must assign empty instead
    	piChunkList list_of_chunks = empty
    	float value = 0.0
    	current as 0

    	for if current < max_iterations
    		current = current + iterations_per_thread

    		int max = current
    		if max > max_iterations
    			max = max_iterations

    		min as current - iterations_per_thread
    		PiChunk chunk = (min, max, value)
    		list_of_chunks at end = move chunk
        
    	return list_of_chunks


    fn int ask_for_threads()
    	macro_run use_print_consts
    	const int DEFAULT = 8

    	for if true
    		# You will notice that prompt is not passed literally, the reason why is Deor as part of the
    		#  "Human-Readability" philosophy: use named-variables for functions with more than 1 argument
    		prompt as s_join(["How many thread-tasks would you like to run? (default ", c_int_to_string(DEFAULT)])
    		print(prompt, SEPARATOR)
    		# Inputs split on spacing to provide arguments, first is the first arg, second the second and so on.
    		#  There is also a input_string and input_list that gives the full string and argument list.
    		(first) in input()

    		# Libs are included with Deor that wrap rust blocks allowing raw access to rust features, these 
    		#  libs are prefixed with a letter, here it is the convert lib c_ to not pollute the global space
    		# When assigned SmallPositive is validated against its predicate you saw earlier
    		SmallPositive input_value = c_string_to_int(first)

    		if first is NONE
    			return DEFAULT
    		#  Here the is valid checks that SmallPositive predicate; an 'avow' unwraps the primitive if valid
    		#  If we are wrong with avow it will crash -- hence the strong wording 'avow'.
    		else if input_value is valid
    			return avow input_value
    		else
    			# Print can take a string or any other primitive, even a literal string if it is 1 argument
    			print("Incorrect entry, must be >0 and the max is 32")


    fn int ask_for_iterations()
    	macro_run use_print_consts
    	const int DEFAULT = 10

    	# Something not mentioned yet is that there is no `while`; for = loop, so "for if" = while
    	for if true
    		prompt as "How many MILLIONS of iterations do you want to run? (default 10 xMILLION"
    		print(prompt, SEPARATOR)
    		(first) in input()

    		LargePositive iterations = c_string_to_int(first)

    		if first is NONE
    			return DEFAULT
    		else if iterations is valid
    			return avow iterations
    		else
    			print("Incorrect entry, must be >0 and the max is 100000 (in MILLIONS)")


    # Deor is picky about case as it is key to readability: consts are SCREAMING_SNAKE, primitives are one  
    #  word lowercase, shapes are camelCase, structs and enums PascalCase, functions/variables snake_case. 
    # The idea is logic uses snake_case and structure uses Pascal or camelCase.
    fn float total_final_results(piChunkList results)
    	float net_value = 0.0

    	# Here the for loop will iterate over every item in the list
    	for res in results
    		(min, max, value) in res
    		net_value = net_value + value
        
    	return net_value


    fn void main()
        
    	# Deor supports _ separators for readability in numbers, they can be put anywhere and are dropped
    	const float MILLIONS = 1_000_000.0
    	const float LIEBNIZ_COEFFICIENT = 4

    	threads as ask_for_threads()
    	float_threads as c_int_to_float(threads)
    	iterations as ask_for_iterations()
    	float_iterations as c_int_to_float(iterations) * MILLIONS

    	iterations_per_thread as m_floor(float_iterations/float_threads)

    	total_iterations as iterations_per_thread * threads
    	print("Performing " + c_int_to_string(total_iterations) + " iterations")
        
    	# Multithreading makes use of a task system, a wrapped rust library that exposes these features
    	#  It is very basic in use and requires a list of the substituted value (T-sub you saw earlier)
    	TaskPool pool = t_pool_make()
    	list_of_chunks as build_chunk(total_iterations, iterations_per_thread)
    	# When complete, it will output that same list-type as a result when all threads finish
    	piChunkList results = t_pi_chunk_run_all(pool, list_of_chunks, compute_pi_chunk)
        
    	net_value as total_final_results(move results)
    	float pi_value = LIEBNIZ_COEFFICIENT * net_value
    	print(s_join(["Pi was calculated to be: ", c_float_to_string(pi_value)]))