Click here to Skip to main content
13,000,022 members (49,249 online)
Click here to Skip to main content
Add your own
alternative version


154 bookmarked
Posted 8 May 2011

Bird Programming Language: Part 1

, 1 Jan 2013
Rate this:
Please Sign up or sign in to vote.
A new general purpose language that aims to be fast, high level and simple to use.

Articles About Bird

Table of Contents 


I'm developing this language because I don't find the other languages perfect for everything. C++ is known to be one of the fastest languages, but its syntax (especially header files) and the lack of C# like high level features makes developing slower in my opinion. Debugging can be also hard in C++. But C# is a managed language and it limits low level programming and application performance. 3D graphics is about 1.5-2 times slower in C# than in C++.

I have been working on Bird since March 2010 in C#. It's a strongly typed native language. Its performanceeseems to be competitive with C++ compilers currently and it's going toand it's going to have features from high level languages besides new things. There are many things that I haven't implemented yet, but it can be used for smaller programs. The syntax is similar to C# and C++ with some modification in order to make code smaller and improve readability. I was planning to make a C# parser too, but I stopped working on it for now. I will start working on a new in the future. The libraries are similar to .NET, the basic functions are going to be implemented. So I think it won't be hard to understand.

Requirements for Running a Program

Samples have a C++ equivalent code to compare performance. In order to compile them MinGW, Clang are needed to be installed and set in the PATH variable, but it's optional. Visual C++ compiler usage requires the path to "vcvarsall.bat" in to be set in "Run - VC++.bat" files.

Creating Programs with Bird

The compiler can be run from command line by "Bird.exe" which is in the "Binaries" directory:

Bird.exe -x -nodefaultlib -lBirdCore -entry Namespace.Main Something.bird -out Something.exe

I've made .bat files for the samples, so using command line for them is not needed. The -x means that the compiler should run the output file after it had been compiled. The input files can be Bird files, C, C++ or Object files.

Libraries can be specified by the -l option. Currently the BirdCore and BlitzMax are available that are included by default. The -nodefaultlib disables them. BlitzMax is another programming language, its functions are needed for graphics because I haven't implemented them yet.

Object and archive files also can be the output file to use it in other languages. It can be specified with the -format attribute. These are its possible values:

appExecutable file
arcArchive file, it doesn't contain the libraries, they need to be linked to the exe.
objObject file, only contains the .bird files' assembly. The other files and libraries are not included.


A Simple Function

using System

void Main()
    Console.Write "Enter a number: "
    var Number = Convert.ToInt32(Console.ReadLine())
    if Number == 0: Console.WriteLine "The number is zero"
    else if Number > 0: Console.WriteLine "The number is positive"
    else Console.WriteLine "The number is negative"
    for var i in 1 ... 9
        Console.WriteLine "{0} * {1} = {2}", i, Number, i * Number

    Console.WriteLine "End of the program"

The indication of code blocks are done based on the whitespaces in front of lines. One scope always have the same number of whitespaces. Colon can be used to make the command able to have the inner block in the same line. The compiler needs to know where the previous expression ends. If there's no expression, like the else statement without if, the colon is not needed.

Functions can be called without brackets, if the returned value is not used. In the for loop the var keyword means the type of i, which is the same as the initial value (1 -> int). The three dots means that the first value of i is 1, and it includes the value at the right side, so the last value is 9.

I was thinking about making able to declare variable without type (or the var keyword), but it could lead to bugs if the name of the variable is misspelled.


Number literals can have different radix and type. $ means hexadecimal, % means binary. Hexadecimal letters have to be uppercase to distinguish them from the type notation, which is the lowercase short form of the type at the end of number:

$FFb        // An unsigned byte
-$1Asb      // A signed byte
%100        // A binary number

Chained Comparison Operators

I think that this could have been implemented in C languages, because in some cases it can be useful. Each sub-expression runs only once, so it's also faster that making two relation connected with and.

bool IsMouseOver(int x, y, w, h)
    return x <= MouseX() < x + w and y <= MouseY() < y + h

The relation operators can only face to one direction to make them distinguishable from generic parameters.


It's similar to the using alias directive in C# and the typedef keyword of C++, but in Bird aliases can be created for everything, even for variables. The order of declaration doesn't matter, so it's possible to do this:

alias int32 = int
alias int64 = long
alias varalias = variable

int64 variable


Tuple Basics

Tuples are similar to structs but they don't have name. Unlike .NET tuple, Bird tuples are value types. Tuples are a grouping of named or unnamed values that can have different types. For example:

var a = (1, 2)            // Type: (int, int)
var b = (1, 2f)           // Type: (int, float)
var c = ("Something", 10) // Type: (string, int)

In this case the reference of members are done by the index of it (e.g. a_tuple.0), but they can get a name:

alias vec2 = (float x, y)

const var v = (2, 3 to vec2
const var vx = v.x
const var vy = v.y

These variables can be declared as constant because the compiler interprets (2, 3) as a single constant.

Tuples can be also used to swap the values of variables:

a, b = b, a

Tuples as Vectors

Vector operations are based on tuples. The SSE/SSE2 packed instructions will be emitted by tuple operations instead of vectorization. The Cross function can be written as:

alias 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.x 

Vector function will be defined in the Math class, some of them are already implemented. Without using the float3 type, this is how it can be written using unnamed members:

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

Tuple Extraction

It's possible to extract a tuple in a similar way as swapping variables:

float x, y, z
x, y, z = Cross(a, b)

Or if var is used, it can be written in a single line:

(var x, var y, var z) = Cross(a, b)

The var have to be written before all the variable in order to make the compiler able to decide which is an existing variable. E.g. if (var x, y, z) = ...  would be interpreted as three new variable then it wouldn't be possible to refer to an existing y, z.

For Loops

for var i in 0 ... 9
for var i in 0 .. 10

Both loops mean the same. Two dots means that i won't have the value at right, in case of three dots it will have that value.

for var x, y in 0 .. Width, 0 .. Height

This is the same thing as two nested loops. The x goes from 0 to Width-1, the y goes from 0 to Height-1. The loop with y variable is the inner one. The break command exits from both.  It can be also written like this:

for var x, y in (0, 0) .. (Width, Height)

If only one number is specified then it will be the initial or the final value of all variables. So this is the same as the previous:

for var x, y in 0 .. (Width, Height)

If there is two point, it's possible to make a single for loop that runs with all points that are in their rect. In this case the x variable goes from P1.0 to P2.0, the y goes from P1.1 to P2.1:

var P1 = (10, 12)
var P2 = (100, 110)

for var x, y in P1 ... P2
    / Something

The step can be used to specify how much the loop variables are increased. It can be both a scalar or a tuple with the same rules. It adds 1 to i and 2 to j at every cycle. The next loop increases i with 1 and j with 2:

for var i, j in 1 .. 20 step (1, 2)

Other Loops

The while, do-while loop is similar to C languages:

var i = 1
while i < 100
    i *= 2

i = 1
    i *= 2
while i < 100

 I created two new that the code can be written smaller with. The repeat does something as many times as specified in the parameter. the cycle makes an infinite cycle.


Structures can contain fields, methods, constructors, etc. The new operator, if the type is not specified, it creates an object with the same type as it is converted to. In this program it is the return type. The original is the var type that is always automatically changed to another type:

struct Rect
    public float X, Y, Width, Height

    public Rect(float X, Y, Width, Height)
        this.X = X
        this.Y = Y
        this.Width = Width
        this.Height = Height

    public Rect Copy()
        return new(X, Y, Width, Height)

    public Rect Copy_2()
        return new:
            X = this.X
            Y = this.Y
            Width = this.Width
            Height = this.Height

    public float GetValue(int i)
        switch i
            case 0: return X
            case 1: return Y
            case 2: return Width
            case 3: return Height
            default return 0

I would note that there is never need to use the break command at the end of the case block. But I'm not sure that there is need for the switch statement, I never use it,

conditions are much more simple in my opinion, especially in C# where the case block must be leaved with some jumping command.


The most important .NET functions have been implemented. I haven't made a GC yet, so objects will remain allocated until the application exits. It's not a problem for now.

using System

void Main()
    Console.WriteLine "adfdfgh".PadRight(10) + "Something"
    Console.WriteLine "adfdh".PadRight(10) + "Something"
    Console.WriteLine "adfdfgh".Contains("fdf")
    Console.WriteLine "adfdfgh".Contains("fdfh")
    Console.WriteLine "adfdfgh".Replace('d', 'f')
    Console.WriteLine "adfdfgh".Replace("d", "ddd")
    Console.WriteLine "adfdfghléáőúó".ToUpper()


Reference Typed Arrays

This is how 1D reference array can be declared and initialized:

var Array1D_1 = new int[234]
var Array1D_2 = new[]: 1, 2, 3

var Array1D_3 = new[]:
var Array1D_4 = new[]:
    1, 2
    3, 4

The compiler takes into account how many dimension are there before interpreting the initial values. The values can be separated with both brackets and new lines. If it founds one less dimensions than specified, the new lines are dimension separators too. I'm not sure it's good, I may remove it the future because it's a bit ambiguous. But it can be also made with using only brackets.

var Array2D_1 = new[,]: (1, 2), (3, 4)

var Array2D_2 = new[,]:
    1, 2
    3, 4
var Array2D_3 = new[,]:
    (1000, 1001, 1002, 1003, 1004, 1005
     1006, 1007, 1008, 1009, 1010, 1011)
    (2000, 2001, 2002, 2003, 2004, 2005
     2006, 2007, 2008, 2009, 2010, 2011)

Fixed Size Arrays

These are value types and stored on the stack. Their type is marked with the size unlike reference arrays (e.g. int[10]). This is how can they be created:

int[5] Arr1 = new
int[5] Arr2 = default

The default keyword is the same as in C#. It's just optional to specify the type if it can be inferred. In this case it is the same as the destination variable. The same thing happens with

, it would be new (int[5])(). The new for value types means the same as default. All values in both arrays are initialized to zero. Initial value can be specified as:

var FixedArr1D = [0, 1, 2, 3]      // Type: int[4]
var FixedArr2D = [(0, 1), (2, 3)]  // Type: int[2, 2]
byte[4] FixedArr1D_2 = [0, 1, 2, 3]

The FixedArr1D_2 array can be declared without an error, because the compiler takes the type of the variable into account before evaluating the initial value.
Fixed size arrays can be converted to reference types with an implicit conversion:

double[] Arr = [0, 1, 2]
Func [0, 1, 2, 3]

void Func(double[] Arr)
    // Something
long[], byte[] GetArrays()
    return [0, 1, 2], [2, 3, 4, 5]

Pointer and Length

The notation of this kind of array (or rather tuple) is T[*] (T is a arbitrary type), that is actually a short form of

	uint_ptr Length)
. It can be useful for unsafe programming. I created it because I had to write two variables for the same purpose. Both reference type and fixed size arrays can be converted to it implicitly:

using System

void OutputFloats(float[*] Floats)
    for var i in 0 .. Floats.Length
        Console.WriteLine Floats[i]

void Main()
    OutputFloats [0, 1, 2]

    var Floats = Memory.Allocate(sizeof(float) * 3) to float*
    for var i in 0 .. 3: Floats[i] = i + 10.5f
    OutputFloats (Floats, 3)

Parameters with ref, out

Using ref it's possible to use a parameter as input and output. The out can be used for only output, but it makes sure that the variable gets a value:

using System

void OutputFunc(ref int x)
    Console.WriteLine x

void Func(out int x)
    x = 10

void Main()
    Func out var x
    OutputFunc ref x
    OutputFunc ref x
    OutputFunc ref x

A variable passed with ref must have a value before the function is called, out parameters must be set to a value before leaving the function. These checks can be bypassed with unsafe_ref.

Named and Optional Parameters

Only parameters that have to be specified are that don't have default value:

// The definition of BlitzMax.Graphics
IntPtr Graphics(int Width, Height, Depth = 0, Hertz = 60, Flags = 0)

Graphics 800, 600
Graphics 800, 600, 32

With named parameters, the earlier parameters are not need to be specified:

Graphics Width: 800, Height: 600
Graphics 800, 600, Hertz: 75

Properties and Indexers

They are marked with colon. Properties are handled as variables, when using them the compiler calls the set and get methods. In case of indexer parameters can be specified too:

class Class
    int _Something
    public int Something:
        get return _Something
        set _Something = value
    public int AutomaticallyImplementedProperty:
    public int this[int Index]:
        get return Index * 2

    public int NamedIndexer[int Index]:
        get return this[Index]
void Main()
    Class Obj = new
    Console.WriteLine Obj[3]
    Console.WriteLine Obj.NamedIndexer[4]

Operator Functions

Operators can be defined for structures and classes that wouldn't allow it by default:

class Class
    int _Something
    public static void operator ++(Class Obj)
void Main()
    Class Obj = new

Getting the Address of a R Value

Sometimes a parameter have to be passed with a pointer to it. In Bird, the address can be queried from constants and R values too, and it automatically copies to a variable:

using System

/* The constructor of Array class:
   public Array(IDENTIFIER_PTR ArrayType, uint_ptr[*] Dimensions,
                uint_ptr ItemSize, void* InitialData = null) */

int[,] CreateIntArray2D(uint_ptr Width, Height)
    var Obj = new Array(id_desc_ptr(int[,]), [Width, Height], 4)
    return reinterpret_cast<int[,]>(Obj)

int[] CreateIntArray1D()
    const uint_ptr Length = 16
    uint_ptr[*] Dimensions = (new: Pointer = &Length, Length = 1)
    var Obj = new Array(id_desc_ptr(int[]), Dimensions, 4)
    return reinterpret_cast<int[]>(Obj)

The type of [Width, Height] expression is uint_ptr[2], so when it casted to

the compiler have to query the address. So it creates a new variable that will be assigned to [Width, Height] and it gets the address of this variable. It does the same with &Length in the second function. reinterpret_cast basically does nothing, it just changes the type of an expression node like casting a pointer.

Reference Equality Operator

The === and !== operator can be used to compare the references of objects. It does the same thing as the

. The == can be also used for this, but it can be overwritten with an operator function.

public bool StringReferenceEquals(string A, B)
    return A === B

Higher Order Functions

The type of a function can be marked with ->. At the left side there are the input parameters, at the right side the output parameters. The calling convention and modifiers also can be specified. E.g.

string, object -> int, float
. When there are multiple outputs, the return type becomes a tuple. In the future I plan to allow all functions to have multiple output in a similar way.

using System

int GetInt(int x)
    return x + 1
int Test((int -> int) Func)
    return Func(2)

void Main()
    var Time = Environment.TickCount
    var Sum = 0

    for var i in 0 .. 100000000
        Sum += Test(GetInt)

    Time = Environment.TickCount - Time
    Console.WriteLine "Time: " + Time + " ms"
    Console.WriteLine "Sum: " + Sum

This little sample shows how it works. I made it in C# too, and these are the performance result with my machine:

Time719 ms2234 ms

Actually it is implemented very simply. Higher order functions are just a tuples of an object and a function pointer

(object Self, void* 
. The Self member can be null if the function is static. It's possible to create a static function pointer with the
keyword: static int -> float. When a nonstatic function is called, the Pointer member is converted to a function pointer. If the Self is not null, it is also added to the parameters. This is how the Test function is extracted:

int Test((int -> int) Func)
    return if Func.Self == null: (Func.Pointer to (static int -> int))(2)
           else (Func.Pointer to (static object, int -> int))(Func.Self, 2)

To make it run faster, the parameter can be replaced to a function pointer. It runs in 542 ms in this way.

int Test((static int -> int) Func)
    return Func(2)


  • 1/1/2013: Higher order functions, stack alignment, and many refactoring
  • 10/11/2012: Scope resolution operator, changed casting operator, the to keyword has the same syntax as is and as operator and it doesn't allow ambiguous code.
  • 22/9/2012: Parameter arrays, better x86 performance
  • 18/8/2012: Implemented stackalloc, pointer and length arrays, new and default without specifying the type, static constructors, checked, unchecked, generic parameters with <> (only at reinterpret_cast)
  • 18/7/2012: Object type casting, boxing, unboxing, is as xor operator, low-level reflection, improved x86 code generation
  • 16/6/2012: Exception handling, try-catch-finally, constants also can be declared inside a function
  • 19/5/2012: Arrays, object initializations, address can be taken of r values, ref, out parameters, added Visual C++ compilation of samples
  • 2/5/2012: Improved performance, identifier aliases instead of typedefs, strings, reference equality operator (===, !==), binary files can be linked into the assembly, changed the name from Anonymus to Bird


This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


About the Author

Dávid Kocsis
Software Developer
Hungary Hungary
No Biography provided

You may also be interested in...

Comments and Discussions

GeneralThanks for sharing Pin
Somnath_Mali17-Jun-12 20:15
memberSomnath_Mali17-Jun-12 20:15 
GeneralDownload Pin
Dávid Kocsis16-Jun-12 19:55
memberDávid Kocsis16-Jun-12 19:55 
GeneralMy vote of 5 Pin
SoMad16-Jun-12 19:04
memberSoMad16-Jun-12 19:04 
GeneralRe: My vote of 5 Pin
Dávid Kocsis16-Jun-12 19:50
memberDávid Kocsis16-Jun-12 19:50 
QuestionNamespaces Pin
umlcat25-May-12 7:17
memberumlcat25-May-12 7:17 
AnswerRe: Namespaces Pin
Dávid Kocsis26-May-12 21:52
memberDávid Kocsis26-May-12 21:52 
AnswerRe: Namespaces Pin
Dávid Kocsis18-Jul-12 6:08
memberDávid Kocsis18-Jul-12 6:08 
QuestionMy vote of 5 Pin
Filip D'haene19-May-12 0:45
memberFilip D'haene19-May-12 0:45 
AnswerRe: My vote of 5 Pin
Dávid Kocsis19-May-12 0:48
memberDávid Kocsis19-May-12 0:48 
QuestionWhat is the future? Pin
Pascal Ganaye4-May-12 1:36
memberPascal Ganaye4-May-12 1:36 
AnswerRe: What is the future? Pin
Dávid Kocsis4-May-12 18:06
memberDávid Kocsis4-May-12 18:06 
GeneralRe: What is the future? Pin
Pascal Ganaye5-May-12 6:49
memberPascal Ganaye5-May-12 6:49 
GeneralRe: What is the future? Pin
Dávid Kocsis5-May-12 10:21
memberDávid Kocsis5-May-12 10:21 
GeneralRe: What is the future? Pin
KenBeckett7-May-12 9:20
memberKenBeckett7-May-12 9:20 
I really think it would be a lot smarter to leverage the .NET platform, which includes an optimized GC, a full IDE, massive libraries, and tons of other things that you need. It is specifically designed from the ground up to support new languages like Bird. You should base your language on .NET to start, then you can always make an independent version later on if usage actually takes off. Also, contrary to another post of yours, .NET really is cross-platform using Mono. It's not perfect, but it works and there is source code.

I also don't understand why you seem to be working on a C# compiler... why? You should bootstrap your new language by writing it in itself rather than another language - this will force you to make it a full featured language that can do everything you need to compile it with itself. Writing one language is a big enough task, but C# is a VERY complex language - read the (500 page) C# 4.0 spec carefully at least twice before you start. If you're aiming for C# 4.0, that is a HUGE effort to replicate - it's a very complex language with tons of interactions between features. Type inference and method overload resolution are each easily many months of work to get right (I know what I'm talking about - I've done them). Also, do not assume that big spec is 100% right - it's not. A few things are missing and/or just incorrect.

Anyone who creates their own computer language is crazy. But, if they don't leverage .NET and try to create their own C# compiler at the same time, they're a lunatic. Smile | :)
GeneralRe: What is the future? Pin
Dávid Kocsis26-May-12 21:21
memberDávid Kocsis26-May-12 21:21 
GeneralMy vote of 5 Pin
Petr Abdulin2-May-12 20:26
memberPetr Abdulin2-May-12 20:26 
GeneralRe: My vote of 5 Pin
Dávid Kocsis4-May-12 17:42
memberDávid Kocsis4-May-12 17:42 
GeneralMy vote of 5 Pin
paladin_t2-May-12 15:24
memberpaladin_t2-May-12 15:24 
GeneralRe: My vote of 5 Pin
Dávid Kocsis2-May-12 19:15
memberDávid Kocsis2-May-12 19:15 
QuestionNice loops Pin
Pascal Ganaye2-May-12 11:55
memberPascal Ganaye2-May-12 11:55 
AnswerRe: Nice loops Pin
Dávid Kocsis3-May-12 6:27
memberDávid Kocsis3-May-12 6:27 
QuestionNice Job Pin
vbfengshui2-May-12 11:08
membervbfengshui2-May-12 11:08 
AnswerRe: Nice Job Pin
Peter_Olson2-May-12 11:40
memberPeter_Olson2-May-12 11:40 
GeneralRe: Nice Job Pin
vbfengshui2-May-12 12:08
membervbfengshui2-May-12 12:08 
GeneralRe: Nice Job Pin
PIEBALDconsult2-May-12 12:39
memberPIEBALDconsult2-May-12 12:39 
AnswerRe: Nice Job Pin
PIEBALDconsult2-May-12 12:36
memberPIEBALDconsult2-May-12 12:36 
AnswerRe: Nice Job Pin
Dávid Kocsis2-May-12 19:35
memberDávid Kocsis2-May-12 19:35 
QuestionSooo... Pin
PIEBALDconsult2-May-12 10:31
memberPIEBALDconsult2-May-12 10:31 
AnswerRe: Sooo... Pin
Zac Greve2-May-12 10:58
memberZac Greve2-May-12 10:58 
AnswerRe: Sooo... Pin
Dávid Kocsis2-May-12 18:56
memberDávid Kocsis2-May-12 18:56 
QuestionWhy create yet another compiler? Pin
Abu Mami23-Apr-12 23:10
memberAbu Mami23-Apr-12 23:10 
AnswerRe: Why create yet another compiler? Pin
Dávid Kocsis24-Apr-12 1:44
memberDávid Kocsis24-Apr-12 1:44 
QuestionWhat about writing a compiler Pin
Jacek Gajek20-Apr-12 10:22
memberJacek Gajek20-Apr-12 10:22 
AnswerRe: What about writing a compiler Pin
Dávid Kocsis20-Apr-12 19:31
memberDávid Kocsis20-Apr-12 19:31 
GeneralRe: What about writing a compiler Pin
Jacek Gajek21-Apr-12 3:57
memberJacek Gajek21-Apr-12 3:57 
GeneralRe: What about writing a compiler Pin
Dávid Kocsis21-Apr-12 23:22
memberDávid Kocsis21-Apr-12 23:22 
GeneralRe: What about writing a compiler Pin
Jacek Gajek22-Apr-12 8:58
memberJacek Gajek22-Apr-12 8:58 
GeneralRe: What about writing a compiler Pin
Miguelit024-Apr-12 5:45
memberMiguelit024-Apr-12 5:45 
GeneralRe: What about writing a compiler Pin
Dávid Kocsis27-Apr-12 5:27
memberDávid Kocsis27-Apr-12 5:27 
GeneralNot Rating Until I Read It... but Pin
Patrick Harris20-Apr-12 8:50
memberPatrick Harris20-Apr-12 8:50 
SuggestionWell done Pin
Jani Giannoudis20-Apr-12 4:19
mvpJani Giannoudis20-Apr-12 4:19 
GeneralRe: Well done Pin
Dávid Kocsis20-Apr-12 4:34
memberDávid Kocsis20-Apr-12 4:34 
Zac Greve6-Apr-12 17:55
memberZac Greve6-Apr-12 17:55 
Dávid Kocsis6-Apr-12 19:16
memberDávid Kocsis6-Apr-12 19:16 
QuestionAmazing :) Pin
Neeraj Durgapal31-Mar-12 6:56
memberNeeraj Durgapal31-Mar-12 6:56 
AnswerRe: Amazing :) Pin
Dávid Kocsis31-Mar-12 22:11
memberDávid Kocsis31-Mar-12 22:11 
QuestionRe: Amazing :) Pin
Neeraj Durgapal18-May-12 10:02
memberNeeraj Durgapal18-May-12 10:02 
AnswerRe: Amazing :) Pin
Dávid Kocsis19-May-12 0:25
memberDávid Kocsis19-May-12 0:25 
QuestionA brilliant experimental work, and the article is well-written: keep going ! +5 Pin
BillWoodruff30-Mar-12 11:34
memberBillWoodruff30-Mar-12 11:34 
AnswerRe: A brilliant experimental work, and the article is well-written: keep going ! +5 Pin
Dávid Kocsis30-Mar-12 20:28
memberDávid Kocsis30-Mar-12 20:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.170624.1 | Last Updated 1 Jan 2013
Article Copyright 2011 by Dávid Kocsis
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid