Rotary
Rotary is a compiled programming language inspired by Meta's Hack and Nadeo's ManiaScript. It is based, not on practicality, but on *vibes*, but without falling into esoteric languages territory.
<entrypoint>main<= define string mystring = "Welcome to Rotary!" print mystring exit 0 =>
Downloads
Coming soon...
Concepts
Compilation pipeline
Rotary's compilation pipeline works in two steps:
- Your file is read by the Rotary compiler and it writes to an assembly file bearing the same name as your .rotary file, only with '.asm' appended.
- The assembly file is to be assembled with fasm(flat assembler)
Defining variables
To define a variable, use the define
keyword, followed by the type, the name, the equal sign, and the value.
define string hworld = "Hello world!"
Types
- string
Function context
The function context defines how the function needs to be treated by the Rotary compiler.
entrypoint
: the function will be used as the main function.function
: the function is a regular function.assembly
: compilation will not occur for this function and assembly will be written straight to the intermediary assembly file.
Function declaration
To declare a function in Rotary, you need a context, a function name, an opening sign, and a closing sign.
<entrypoint>main<= print "Welcome to Rotary!" exit 0 =>
Here, <entrypoint>
is the context; it defines that the function, main
, is the program's starting function. <=
indicates the beginning of the function, and >
indicates its end. Please note that if multiple entrypoints are defined, only the last one will be considered as the entrypoint for the program. Also please note that, although a function may call another function or even an assembly function, it may not call an entrypoint.
Indentation
Rotary has some rules around indenting.
- You must not indent function declarations or closings.
- You must indent with spaces, not tabs.
Name-following, repeating, and esoteric function declarations
Function declaration in Rotary is at first very forgiving regarding to how you order your functions. Take for example these two totally valid function declarations:
1. Classical function declaration
<entrypoint>main<= print "Welcome to Rotary!" exit 0 =>
2. Name-following function declaration
print "Welcome to Rotary!" exit 0 <entrypoint>main<= =>
The catch? Doing this breaks variable decoration in the intermediary assembly file. Therefore, due to ordering conflicts during the first stage of the compiling pipeline, of these three following examples, only two are valid:
1. Valid name-following function declaration
print "Welcome to Rotary!" exit 0 <entrypoint>main<= => --- Compiles to: format PE console entry main section '.text' code executable _main: cinvoke printf, 'Welcome to Rotary' invoke ExitProcess, 0 [...]
2. Invalid name-following function declaration
define string mystring = "Welcome to Rotary!"
print mystring
exit 0
<entrypoint>main<=
=>
---
Compiles to:
format PE console
entry main
section '.text' code executable
_main:
push __mystring ; should be __main_mystring
call [printf]
pop ecx
invoke ExitProcess, 0
section '.data' data readable
__main_mystring db 'Welcome to Rotary!',0
[...]
3. Valid classical function declaration
<entrypoint>main<=
define string mystring = "Welcome to Rotary!"
print mystring
exit 0
=>
---
Compiles to:
format PE console
entry main
section '.text' code executable
_main:
push __main_mystring ; should be __main_mystring
call [printf]
pop ecx
invoke ExitProcess, 0
section '.data' data readable
__main_mystring db 'Welcome to Rotary!',0
[...]
There is, however, an exception to this type of function declaration; assembly
functions cannot be declared after assembly instructions as it sets a flag within the compiler regarding whether or not it should be compiling the code it sees or let it pass through as assembly.
1. Valid assembly function declaration
This is valid; when it is not told to let lines pass through as assembly, the compiler checks the first token of the line. Since, in this case, it is told to let pass by the assembly context, it doesn't check if cinvoke is a function in Rotary.
<assembly>myFunc<= cinvoke printf, "Welcome to assembly from Rotary!" =>
2. Invalid assembly function declaration
This is invalid; as the compiler checks for the first token, it sees that "cinvoke" is indeed not a proper Rotary function and halts.
cinvoke printf, "Welcome to assembly from Rotary!" <assembly>myFunc<= =>
Another fun side effect of Rotary's compiler ordering is the ability to recontextualise functions
A valid function called "myFunc".
This creates an assembly function called "myFunc". Everything up to the second function declaration is passed through as assembly. Then it is just considered to be a normal function. When the entrypoint recontextualisation hits, the function, which at this point has had both passed-through and compiled code, is considered the entrypoint of the program.
<assembly>myFunc<= [...] <function>myFunc<= [...] <entrypoint>myFunc<= =>
To avoid ordering conflicts whilst using function declaration, be sure to always keep names consistent between multideclarations and to always declare your function at least once before defining your first local variable within it.
Examples
Big "Hello, world!"
<entrypoint>main<= test print "Welcome to Rotary!" exit 0 => <function>test<= define string hworld = "Hello world!" print hworld print "Welcome to Rotary!" asmtest return => <assembly>asmtest<= cinvoke printf,"Welcome to assembly from Rotary!" ret =>