Luasm

From Esolang
Jump to navigation Jump to search

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
]]