Anonymus, the optimal language
Download Examples.zip - 111.7 KB
The Square function draws a square that is white if the mouse is on the square. You can exit from the program with the escape key:
You can also make complex relations with this language. In this way you only call the MouseX and the MouseY function once. If the left condition is false then the other isn’t called.
The LNKO function computes most common divisor of two numbers. I subtract the smaller number from the greater while they aren’t equal:
With flag types you can add meaning to the bits. If a number doesn’t specified then it will be power of 2:
If I want to manage the members of the tuple its enough to separate the components by commas at the left of the assignment:
The Anonymus language allows declaring variables at the return type and the only important thing is that this variable needs to get a value:
Referencing function written in other languages
You can reference them in the extern scope. With asmname you can specify the name that Anonymus manages it in the compiled code. You can also give the calling convention of the function. Currently my language supports cdecl, stdcall, and ascall:
If its succeeded than it should call the plugin that the recognizer received by parameter.
If the expression recognizer wants to declare a variable it should call a plugin’s function. If it can’t do this it gives an error message. The multi plugin redirects the declaration to the compiler plugin, if exists.
In the code scopes can contain commands that also can contain things. Eg an if command have an expression which is the condition and one or more code scopes. A function scope is the most outer scope in a function that is also a code scope.
Introduction
I started this project because I want a programming language which you can develop easy and efficiently with. I’ve been working with C# for about a 1 year. I tried to make the generated assembly code to execute quickly. If like my language you can follow my blog.
Creating programs with Anonymus
To compile the programs written in Anonymus you should run the “As.exe” file. Some functions are used from BlitzMax. You need to download and install BlitzMax demo to run the sample programs. These functions are declared in “BmxLibs.txt”. In the command line you should give the input files first and then the output file with “.s” extension.As.exe BmxLibs.txt Something.txt Something.sThe “Launcher.bmx” runs the AsEntry function. This should import the assembly code created by Anonymus. You can run it with MaxIDE or in command line:
bmk makeapp -x -r -a -t console Launcher.bmxThe “bmk.exe” is located in BlitzMax\bin directory. You should add this to environment variable “PATH” (In Win7: Computer->Properties->Special System Settings->Environment Variables)
1st Sample program: Squares
The task is to draw 9 squares. The square on which the mouse is will be white, the others will be gray. You can compile and run the sample with “Squares\Run.bat” file.The Square function draws a square that is white if the mouse is on the square. You can exit from the program with the escape key:
stdcall void AsEntry()
Graphics 800, 600, 0, 60, 0 ’ Create a graphical interface
while !KeyHit(Keys.Escape) ’ While we don’t hit the escape key
Cls ’ Clear screen
rem If a variable is declared with “var” then the it will be the same
type as the initial value. The for-until loop written in C:
for (int x = 0; x < 3; x++)
for var x = 0 until 3
for var y = 0 until 3
Square 10 + x * 110, 10 + y * 110, 100, 100
Flip -1 ’ Visualize the drawn elements
At the function calls, if we don’t care about the return value then we don’t have to put brackets. You also don’t need to specify the end of the “for”, “while” etc. loops, when anyway we begin the line farther to be more readable.You can also make complex relations with this language. In this way you only call the MouseX and the MouseY function once. If the left condition is false then the other isn’t called.
void Square(int x, y, w, h)
if x <= MouseX() <= x + w and y <= MouseY() <= y + h
SetColor 255, 255, 255
else SetColor 150, 150, 150
DrawRect x, y, w, h
Write this function in C is much harder if you want to avoid repeatedly call of MouseX and MouseY:void Square(int x, int y, int w, int h)
{
bool White = false;
int mx = MouseX();
if (x <= mx && mx <= x + w)
{
int my = MouseY();
if (y <= my && my <= y + h)
White = true;
}
if (White) SetColor(255, 255, 255);
else SetColor(150, 150, 150);
DrawRect(x, y, w, h);
}
2nd Sample program: PerfTest
In this program I compare the performance of the BlitzMax, Gcc (GNU C Compiler) and the Anonymus languages, with simple program which computes the most common divisor repeatedly.The LNKO function computes most common divisor of two numbers. I subtract the smaller number from the greater while they aren’t equal:
int LNKO(int i, j)
while i <> j
if i > j then i -= j
else j -= i
return i
Here is the generated assembly code:_LNKO:
cmp eax, edx ’ Compare i and j
je _1 ’ If they are equal jump to _1
_9:
cmp eax, edx ’ Compare i and j
jle _14 ’ If i <= j then jump label 14
sub eax, edx ’ i -=j
cmp eax, edx ’ Compare i and j
jne _9 ’ If the aren’t equal continue the loop
ret ’ Leave the function
_14:
sub edx, eax ’ j -= i
cmp eax, edx ’ Compare i and j
jne _9 ’ If the aren’t equal continue the loop
_1:
ret ’ Leave the function
The AsEntry function is the entry point. The BeginPerf counts the time until EndPerf function is called. I store a number in the “Ret” variable, if it is equal in all languages then program works good. I compute the most common divisor of two variable 25000×25000 times:stdcall void AsEntry()
BeginPerf
var Ret = 0
for var i = 1 to 25000
for var j = 1 to 25000
Ret += LNKO(i, j)
EndPerf
PrintInt Ret
When I ran the programs I got the following values that show the Anonymus is about 4% faster than Gcc:
Processor | BlitzMax | Gcc | Anonymus |
Intel Core 2 Duo 1.86 Ghz | 115,195s | 100.709s | 96,512s |
Enum Types
The enum types are useful when we want to distinguish codes. In this way just a name is needed to referring with.enum Keys
A = 65, B, C, D, E, F, G, H, I, J, K, L, M, N, O,
P, Q, R, S, T, U, V, W, X, Y, Z,
F1 = 112, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
If the code isn’t specified it will be greater by 1 than the previous. You don’t have to give the first, in this case it will be 0.With flag types you can add meaning to the bits. If a number doesn’t specified then it will be power of 2:
flag Modifiers
None = 0
Shift = 1, Control, Option, System,
Alt = Option, Menu = Option, Apple = System, Windows = System
Tuples
The tuples allow storing multiple values in a variable with different types. You can write the multiplication of vectors in Anonymus language like this:’ I changed this to avoid confusing the new type in some cases type float3 = (float x, y, z) float3 Cross(float3 a, b) return a.y * b.z – a.z * b.y, a.z * b.x – a.x * b.z, a.x * b.y – a.y * b.xIf I don’t define the float3 type or name the members they can be indentified by numbers:
float, float, float Cross((float, float, float) a, b)
return a.1 * b.2 – a.2 * b.1,
a.2 * b.0 – a.0 * b.2,
a.0 * b.1 – a.1 * b.0
For the simple vector operations (addition, subtraction) you don’t have to call functions, its enough to write a + b.If I want to manage the members of the tuple its enough to separate the components by commas at the left of the assignment:
float x, y, z
x, y, z = Cross(a, b)
It’s also possible to declare and give values to the variable, but in this case you have write out the types to be unambiguous:(var x, var y, var z) = Cross(a, b)
Structures
The structures allow storing of related data and functions:struct Rect
float X, Y, Width, Height
’ Constructor: This is called when a Rect is created
Rect(float X, Y, Width, Height)
this.X = X
this.Y = Y
this.Width = Width
this.Height = Height
Rect Copy()
return new Rect(X, Y, Width, Height)
A structure can inherit another structure. If inherited doesn’t have parameter-less constructor then you have to call it:struct Something : BaseStruct
Something(a, b) : base a, b
If I don’t want a structure inheritable the final modifier can be used:final struct Something : BaseStruct
Something(a, b) : base a, b
The members can be static that means the function or variable is global, but it relates to the structure. If all members are static the static classes can be used:static class Console
static void PrintInt(int x)
static void PrintLong(long x)
static void PrintDouble(double x)
Swapping of Variables
It’s swap two or more data simple without used temporary variable:a, b = b, aWithout this the only way to declare new variables or creating a function that does the swapping, but this is also not perfect because the parameter can be only type. In order to be able to output values in parameters I use the ref types. When you pass a parameter with ref you pass the address of the variable not its value:
void SwapInts(ref int a, b)
var c = a
a = b
b = c
’ Usage:
SwapInts ref a, ref b
In C pointers are also needed:void SwapInts(int* a, int* b)
{
int c = *a;
*a = *b;
*b = c;
}<span class="Apple-tab-span" style="white-space: pre; "> </span>
// Usage:
SwapInts(&a, &b);
Function return value
Sometimes it’s useful when the control doesn’t returns to the calling function when a calculated value should be used before returning. An example function from my programming language source code (C#) (Compilers.CodeScopeNode.CreateRetCommand()):RetCommand CreateRetCommand(int Label)
{
var Ret = new RetCommand(this, Label);
State.Arch.OnNewContainer(Ret);
return Ret;
}
This function creates a command that leaves the function. The “Label” parameter is at the end of the function. All commands inherit the IdContainer class. When an IdContainer is created then the OnNewContainer should called.The Anonymus language allows declaring variables at the return type and the only important thing is that this variable needs to get a value:
RetCommand Ret CreateRetCommand(int Label)
Ret = new RetCommand(this, Label)
State.Arch.OnNewContainer Ret
Its possible to create multiple variables, in this case the return type will be a tuple.Referencing function written in other languages
You can reference them in the extern scope. With asmname you can specify the name that Anonymus manages it in the compiled code. You can also give the calling convention of the function. Currently my language supports cdecl, stdcall, and ascall:
extern
cdecl asmname("_brl_polledinput_KeyHit") bool KeyHit(Keys key)
In C theres is no asmname setting, you need to used with the full name or declare a macro:void brl_polledinput_KeyHit(uint KeyCode);
#define KeyHit brl_polledinput_KeyHit
You can also import functions from dlls:import "kernel32.dll" stdcall void ExitProcess(uint uExitCode)The default function call is ascall that uses eax, edx and ecx for parameters, but passes pointers, classes etc. first then the numbers (except the floating point numbers because its not possible to calculate if they are in general registers)
The structure of the language
Identifiers:
These can be types, variable or functions. All variables and functions have types. The latter is determined by the return value and the parameter’s type. The variables are also divided into ground. They can be local, global or member of a structure.Modifiers:
They can be found before the declaration. (eg. cdecl, asmname, etc.). They are isolated and stored in a list that will receive a function what does the declaration. For example if a variable is marked with static than it will create a global identifier.Expressions:
These are parsed by different recognizers. It’s possible to create a new or replace one without a large modification in the language. An expression recognizer can recognize more operation. Eg. The Multiplicative recognizes multiplication, division and modulus.If its succeeded than it should call the plugin that the recognizer received by parameter.
Plugins:
The plugins can modify the recognized expression. The MultiPlugIn class manages multiple plugin joint work. The type manager plugin’s task is to determine the type of the expressions and do the conversions. The calculator plugin calculates a value if we want to operate with constants.If the expression recognizer wants to declare a variable it should call a plugin’s function. If it can’t do this it gives an error message. The multi plugin redirects the declaration to the compiler plugin, if exists.
Preprocessor:
It processes all statements started with #. You can create a macro or if a condition is true run another code. There’s a preprocessor plugin that replaces the expressions created by call recognizer to the given expression:#define min(x, y) if x < y then x else y #define max(x, y) if x > y then x else y int Something(int i, j) return max(i, j) / main(i, j)The preprocessor makes produces this from it:
int Something(int i, j) return (if i > j then i else j) / (if i < j then i else j)
Scopes:
In the Anonymus language statements that have the same number of whitespace characters belongs to a scope. First the compiler creates a global scope, than calls the preprocessor, processes the types and the then the functions and variables. The MTCompiler processes these multi-threaded.In the code scopes can contain commands that also can contain things. Eg an if command have an expression which is the condition and one or more code scopes. A function scope is the most outer scope in a function that is also a code scope.
Architectures:
It’s possible to do more architecture that creates assembly code. If necessary, it can use a plugin or attach data to identifiers, identifier containers. Currently the x86 architecture is implemented.Creating a new language
In the Anonymus environment more languages can be implemented with replacing some recognizers. Its much easier to create a language like this and the functions, types, variables will be available between Anonymus and the new languages.
推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
发表评论
Wow! Finally I got a webpage from where I be capable of truly obtain valuable facts regarding my study and knowledge.