From bb2f4b541cc7da2a97af3c50b0ac25bec1e2be67 Mon Sep 17 00:00:00 2001 From: Dummi26 Date: Wed, 8 Mar 2023 22:19:51 +0100 Subject: [PATCH] initial code commit. variables, functions, basic multithreading works. missing a lot of builtins and possibly operators (+-*/). good proof of concept for now. --- README.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fceab02..2160544 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,76 @@ # mers -Mark's error-script -- A script language with explicit error handling +Mark's error-script -- A script language with explicit error handling and type-checks that run before the script even starts. + +**This is highly experimental and a lot of things might change. It lacks very basic builtins (who needs to add integers together anyways) and has a lot of bugs.** + +## checks run before the script does + +One of the worst things about script languages is writing a script that runs for more than 5 minutes just to crash right before printing the output because of some type-error. +With mers, this won't happen - mers runs checks and parses the script into a special data structure before running it. This ensures that types are always what they should be and all errors can be caught before the script even starts. +(mers was born out of the need for "a script language that handles errors almost as well as Rust, but with less typing") + +## Type-Checked + +The main feature of this language is the type-checking. Here are some common approaches that you might be familiar with: +- Asssign a type or a Trait/Interface/Base Class/... to each variable (most statically typed languages - Java, C#, and Rust) +- Just don't - what could go wrong? (please stop doing this - JavaScript and most Scripts/Shells) + +While the OOP approach is safe and won't cause weird crashes, it requires classes, interfaces or enums, which can be a lot to type for a simple script. + +The JavaScript approach requires way less typing, but is prone to crashing + +In mers, each variable has a type. However, this type is actually a list of all types the variable can have. This means +- v = "this is a string" + + s will always be a string. The type-checker knows this. +- v = if some_condition { "Hello, user!" } else { -1 } + + v could be a string or an integer. The type checker knows this, too, showing its type as [String, Int]. +- v = thread( () "This is an anonymous function returning a string" ) + + Here, we use thread to run an anonymous function in the background. v will have the type Thread(String). If we do r = v.await(), r will be a String. The type-checker knows this (TODO! it should, but currently doesn't. prepare for crashes.). + +To ensure that variables with more than one possible type won't cause issues, **every possible type has to be handeled**. This can, for example, be achieved using to_string(), which accepts arguments of types String, Int, Float, Tuple, or List. +If a variable could be a String, Int or Float and should be multiplied with another Float, the type-checker will complain that the String case isn't handeled because the mul() function doesn't accept String arguments. +To distinguish between different types, a *switch* statement has to be used. To pattern-match on known types, *match* statements can be used. (TODO! Both of these aren't implemented yet. There is currently no way to handle different types.) + +## Error handling + +Using Rust has dropped the number of runtime errors/exceptions in my projects significantly. The same thing should be true for mers. Just like Rust, there are no exceptions. If a function can fail, that will be visible in the return type. +In Rust, loading a file might show Result as the functions return type. In Mers, it would be String/[], where [] is an empty Tuple similar to () in Rust. The Type-Checker will refuse to assume that the function returned String, because it might have returned nothing. +We need to handle the String case explicitly, for example using *switch*. If we don't, the type-checker wont even let the script start, preventing runtime errors. + +## Multithreading + +"Function" is a valid type for variables in mers. +Functions have any number of arguments and can capture their environment. +They can be executed synchronously using run(f, args..) or on a different thread using thread(f, args..). +run() will return what the function returns while thread() will return a Thread value. await(thread) will wait for the thread to finish and return its value. + +## Examples + +### Running a thread and awaiting it, passing arguments to the thread when starting it and sharing a variable because the thread's function captured it (useful for reporting progress, i.e. changing a float from 0.0 to 100.0 while downloading and using the main thread to animate a progress bar, then using .await() only when the float is set to 100 to avoid blocking) + + print( "Starting" ) + + captured = "Unchanged" + + calc_function = (prog_message string) { + sleep( 1 ) + print( prog_message ) + sleep( 3 ) + captured = "changed from a different thread" + "this is my output" + } + + calc_thread = thread(calc_function "progress message!") + + sleep( 2 ) + print( "Done." ) + print( captured ) + + calc_thread.await().print() + print( captured ) + +## Quirks + +currently, f(a b c) is the same as a.f(b c). var.function(args) will use var as the function's first argument, moving all other arguments back. This removes the need for struct/class syntax. Simply declare a function scream(str string) { str.to_upper().print() } and you can now use var.scream() on all strings. + +function(var) will break the parser, every argument has to be followed by a whitespace (space, tab, newline, ...). This will hopefully be fixed soon, but for now, function(var ) or just var.function() is the syntax that works.