Luasm
Luasm
Luasm: The mid-level Lua compiler, made by User: Alx. (Aliases: LLua, LuaW, LuaF)
Luasm - What is it?
Luasm, as the name suggests, is a mid-level Lua derivative, designed with performance in mind, and a routine for doing mid-level programming in a high-level sense. Luasm aims to be like C, in the way that it is memory-oriented, except without all of the extra confusing jargon which comes along with C, but keeping fundamental practices. In comparison to Lua, there are a number of syntactical differences, and a lot of semantic differences, the main semantic difference being that it is not object-oriented (as aforementioned implicitly). The Luasm compiler utilizes the Lua++ syntax, and is also syntactically compatible, though operators and general flow is extremely different.
Luasm - Semantics
Semantics: Syntactical Differences
In Luasm, there are two more unary operators, being the * (read/write memory) operator, and the & (get memory address) operator. Pointers in Luasm in comparison to C are quite different, in the way that they do not require declaration, and they are arguably not too similar understanding them and how they work. Unlike in C, the read/write memory operator may take any operand as long as it is a valid memory address, and then read from / write to that position in memory. E.G:
-- Initialize value to 1, write value `123` to the address of value, print value, and memory address of value. function main(PE_CONSOLE) value = 1 -- [[ you could also construct C-like pointers like so: value = 1 ptr = &value *ptr = 1 -- write value at memory address ptr = &othervalue -- change memory address ptr points to ]] *(&value) = 123 printf("%d, %d\n", value, &value) end
In Luasm, you can also `create types`, by using the struct keyword, and then instantiate it via a function call. E.G:
function main(PE_CONSOLE) struct "structure" --[[must be string and not identifier for parsing purposes]] { member_a; member_b; } instance = structure(123, "456") -- the memory address of the string will be member_b. printf("%d\n", instance.member_a) -- 123 printf("%s\n", instance.member_b) -- 456 end
In Luasm, inplace arithmetic operators also are supported. E.G:
function main(PE_CONSOLE) v = 1 while v < 10 do printf("%d\n", v) v += 1 end end
In Luasm, array indexes start at 0. E.G:
function main(PE_CONSOLE) arr = {1, 2, 3} for i=0, 2 do printf("The %d-nth element of the array is %d.\n", i, arr[i]) end end
In Luasm, since array types are always numbers (non-numbers, such as strings, are passed as addresses if defined within the array), strings can be mutated and index using the get_char and set_char functions. E.G:
function main(PE_CONSOLE) string = "hello world" set_char(string, 1, 97) -- // string[1] = 'a'; printf("%s\n", string) end
Semantics: Typing
In Luasm, there exist conceptual types, being number, string and array. There is no static typing, or type declaration in Luasm, a partial reason being binary width. Here is an example of instantiating such types:
function main(PE_CONSOLE) my_number = 1234 -- // uint32_t my_number = 1234; my_array = {1, 2, my_number} -- // uint32_t my_array[] = {1, 2, my_number}; my_string = "hello" -- // uint8_t my_string[] = "hello"; my_empty_string = new_string(256, 0) -- preprocessor directive // uint8_t my_empty_string[256]; my_empty_array = new_array(256, 0) -- preprocessor directive // uint32_t my_empty_array[256]; end
As aforementioned, you can create and instantiate your own `types` as structs.
Semantics: Microsoft API Compatibility
The Microsoft API (32 bit) utilizes the __cdecl calling convention, in which the caller clears up the stack, as opposed to the callee, which would be __stdcall. Due to vararg functions, it is assumed that every function uses the __cdecl calling convention unless explicitly declared otherwise. A Microsoft API function also usually must be bound to the memory-indexing invoking method, in which the __mcall directive tells the compiler to use such an invocation method. E.G:
function main(PE_CONSOLE) import "time" __mcall(time) -- // from msvcrt import "srand" __mcall(srand) srand(time(nil)) end
Semantics: Importation and Inclusion
There exist two preprocessor directives to import modules, being the "import" directive and the "include" directive. The include directive expects a FASM source file path, which is statically appended, whereas the import directive expects a function name to import. If not found in msvcrt, a library must be prepended to the name, via a dot. E.G: import "library.function" . I forgot to write support for Luasm source-including, I'll probably add that soon.
Semantics: Entry point
The entry point must be named `main`, and must have a parameter of either PE_CONSOLE, or PE_GUI, to specify the application interface type.
Luasm - How does it work?
Luasm compiles straight to FASM/A (Flat Assembler Assembly), via an AST. The code is generated through recursive node visiting, and utilizing the stack if no node optimizations can occur, which in turn creates very inefficient, redundant and slow assembly code. A redundant instruction omitter and peephole optimizer will organise, and optimize the code, ensuring that most redundancies and slow instruction sequences are rectified and improved.
- Luasm is explicitly 32-bit, therefore using 8/16/32 bit registers, and operands.
- Return values for Luasm functions are passed through the EAX register, changed from moving to a DWORD RETURN memory address belonging to the function.
- The most common registers used are EAX, EBX and ESP.
- Luasm utilizes the __cdecl calling convention.
Luasm - Example Programs
Hello World
function main(PE_CONSOLE) printf("Hello world!\n") end --[[ STDOUT: Hello world! ]]
99 Bottles of Beer
function main(PE_CONSOLE) bcount = 99 while bcount > 0 do if bcount > 1 then printf("%d bottles of beer on the wall,\n%d bottles of beer\n", bcount, bcount) printf("Take one down, pass it around,\n") bcount = bcount - 1 printf("%d bottles of beer on the wall.\n\n", bcount) elseif bcount == 1 then printf("%d bottle of beer on the wall,\n%d bottle of beer\n", bcount, bcount) printf("Take one down, pass it around,\n") bcount = bcount - 1 printf("No more bottles of beer on the wall.\n") goto end_beer end end ::end_beer:: end --[[ STDOUT: 99 bottles of beer on the wall, 99 bottles of beer Take one down, pass it around, 98 bottles of beer on the wall. 98 bottles of beer on the wall, 98 bottles of beer Take one down, pass it around, 97 bottles of beer on the wall. 97 bottles of beer on the wall, 97 bottles of beer Take one down, pass it around, 96 bottles of beer on the wall. ... 3 bottles of beer on the wall, 3 bottles of beer Take one down, pass it around, 2 bottles of beer on the wall. 2 bottles of beer on the wall, 2 bottles of beer Take one down, pass it around, 1 bottles of beer on the wall. 1 bottle of beer on the wall, 1 bottle of beer Take one down, pass it around, No more bottles of beer on the wall. ]]
ROT13 Cipher
function rot13(straddr) for i=0, 0ffffh do char = get_char(straddr, i) if char == 0 then goto end_rot elseif (char < 96) & (char < 123) then goto start_rot_again end sum = char + 13 if sum > 122 then sum = (sum % 123) + 97 end set_char(straddr, i, sum) ::start_rot_again:: end ::end_rot:: return 1 end function main(PE_CONSOLE) text = "hello world!\n" rot13(text) printf(text) rot13(text) printf(text) end --[[ STDOUT: uryyb jbeyq! hello world! ]]
Truth machine
function main(PE_CONSOLE) d = new_string(2, 0) scanf("%d", &d) if d == 1 then while true do printf("1") end elseif d == 0 then printf("0") end end --[[ STDIN: 1 STDOUT: 1111111111... STDIN: 0 STDOUT: 0 ]]
Palindrome
function main(PE_CONSOLE) v = new_string(1024, 0) scanf("%s", v) printf(v) c = 1024 while c >= 0 do printf("%c", get_char(v, c)) c -= 1 end end --[[ STDIN: hello STDOUT: helloolleh ]]