This is a compiler for a custom programming language written entirely in 16-bit assembly (TASM ideal mode) for a school project.
It is not intended for actual use.
In order to build and run the project, you need to download a DOS emulator (I used and will provide instructions for DOSBox) as well as TASM.
Clone the project, and copy the contents of the folder that contains the TASM.EXE
file into its root.
In DOSBox, enter the following commands:
mount c: the\absolute\path\to\the\project
c:
cycles = max
compile
(If you don't close DOSBox after doing this once, you only need to run compile
to execute the program.)
You will now be asked which file you want to compile. Enter its path (for example, samples\hello.al
)
If there were any errors, the program will at this point exit with an error message and a line number.
If you encounter an infinite flashing cursor on a black screen with no ability to receive any input, it means the compiler ran out of memory (we only have 64 kilobytes to work with!). Reduce the file length.
If everything completed successfully, you will be asked where to store the output file (for example, hello.asm
).
You should be able to successfully assemble and run it using:
tasm <your_output>
tlink <your_output>.obj
<your_output>.exe
There is support of the following constructs:
- Functions that can take arguments and return values
- Global and local variables
- If statements
- While loops
- Simple integer math
- Static typing (beware that there is no casting!)
- Inline assembly code
The syntax is similar to C, with python's type hints, but it requires you to announce what you are doing before actually doing it, so, for example:
my_variable = 5;
would be set my_variable = 5;
and
my_function();
would be call my_function();
note that when using a function's return value, call
should not be used, it should instead be called normally (for example: call print(get_value());
and set value = get_value();
a list of all commands and examples:
- set -
set my_var = 5;
- call -
call my_func(3, false);
- return -
return;
in a void function orreturn value;
in a non-void function. - break (inside of a loop) -
break;
- continue (inside of a loop) -
continue;
- math (calculate simple arithmetic) -
math result = 3 + my_func();
. Supports+
,-
,*
,/
,%
. - inc (increment) -
inc my_var;
- dec (decrement) -
dec my_var;
To leave a comment, prefix the line with //
, for example // my comment.
The comment will end at the end of the line.
In order to declare a function, type (outside of any other functions):
func my_function_name(param1: type, param2: type, ...) -> return_type
{
// function code
}
Possible types are: int, bool, char, string, and void for return_type.
For example:
func cool_function(a: int, b: bool, c: int) -> string
{
// this function does cool stuff!
}
Every project must have a function with the following signature: func main() -> int
. It is the function that will be executed when you run the project.
An if statement is as follows:
if val1 operator val2
{
// stuff
}
else
{
// other stuff
}
The else block is optional. possible operators are: <
, >
, ==
, !=
, <=
, >=
.
For example:
if my_func(3) < 3
{
call do_something();
}
else
{
call do_something_else();
}
Note that braces are mandatory, even if there is only one line, that parentheses around the condition are not supported, that else if does not exist (use else { if condition {} }
instead of else if condition {}
), and that you can't chain conditions using logical operators (for example, &&).
For a while loop:
while val1 operator val2
{
// stuff
}
Read the section about if statements for an explanation about val1 operator val2
.
You can use continue
and break
inside of a loop.
In order to put inline assembly in your program:
asm("my assembly code");
For example, asm("mov ax, 5");
To declare global variables, put anywhere in your program:
data
{
var my_var_name: type;
var my_second_var_name: type;
...
}
They cannot have default values.
Possible types are: int
, bool
, char
, string
.
To declare a local variable, type inside of a function:
local my_var_name: type;
or
local my_var_name: type = value;
Locals are local to a specific function, not to a set of curly braces like in most languages. This fact was an oversight rather than an intended feature or a limitation, and may get changed in the future.
There exists a rather serious bug in which declaring a local inside of a false if statement or a while loop that gets executed any number of times other than 1 will cause your final program to crash.
Every file you write has access to these functions:
- func print(s: string) -> void -- prints a string to the standard output.
- func print_char(c: char) -> void -- prints a single character to the standard output.
- func print_int(i: int) -> void -- prints an integer to the standard output.
- func istr(p: string, i: itn) -> char -- get the character at index i from p.
- func char(l: int) -> char -- constructs a char from its ascii code.
- func digit_to_char(c: int) -> char -- turns a digit into its ASCII representation.
func main() -> int
{
call print("Hello, world!");
return 0;
}
More examples can be found in the samples folder.
- Text in comments must have an even number of quotes and double-quotes, so
// you can't write this comment!
// 'this', however, is valid.
You will not get an error message, but no code after the comment will be compiled (until the next quote/double-quote). - Declaring a local inside of an if statement or a while loop will break things. No error message.
- You can't call a function inside of a while loop's condition. There is an error message for this one.
- There is no error message for an unclosed function. You will get one when assembling, however.