//// Cola Programming Language

The Cola Programming Language

Cola is a modern, statically typed, object-oriented programming language, similar to C#. It is geared towards faster development and more readable code.

I'm a fan of C# and the .NET framework. I also like things about Python, Perl, C++, D and Javascript. I'm a fan of getting stuff done, fast. I'm a fan of using only what I need.

Cola resembles C#. It will compile some C# code. But it isn't strictly compatible. Cola has high level regular expression syntax, simple string comparison operators, standalone functions, omission of function return type (default void), 128-bit decimal type as the default floating point type, auto-allocated arrays, scope-access modifier shorthand, public access by default (for fast prototyping, sorry purists), briefer constructor syntax, and miscellaneous other things that I like. On the TODO list is type pattern matching, C# dynamic, delegates and lambda.

colac can compile real .NET programs right now. However, the compiler is not yet production quality, nor will it be anytime soon. I am learning firsthand to become a better compiler writer.

You can reach me at melvin@cola-lang.org (Happy Thanksgiving!!)

Download Cola »

					
// Cola supports regular expression as a first class language construct.
// Also, using the "as <identifier&rt" we can declare explicit match variables
// and refer back to them.

   var xml =
    @"<book genre='novel' ISBN='1-861001-57-5' misc='sale item'&rt
         <itle&rtThe Handmaid's Tale</title&rt
         <price&rt14.95</price&rt
      </book&rt";
				
   string words[] = {
      "Handmaid", "Tale", "statically", "initialized", "dynamic", "array", "!"
     };

   // Negative match
   for(var word in words)
      if(word !~ /^(Handmaid|Tale)$/)
         io.println(word);

   // Standard regex
   if( xml =~ /(book)(genre)/ ) {         // implicit match backreference
      io.println("In string: " + $0);
      io.println("we matched: " + $1 + ", " + $2);
   }

   // Escaping the regex delimiter if used within regex

   if( xml =~ /(.*?)<\/price>/ )
      io.println("Found price : " + $1); 


   // Use alternate delimiters ## to avoid escaping common forward slash with XML/HTML

   if( xml =~ #(.*?)# )
      io.println("Found price : " + $1); 


   // We can explicitly name our regex matches like declaring a variable
   // in order to use $0, R1, etc. in a nested fashion

   if( xml =~ /()/ as book ) {
      io.println(book.$1);


      // Unqualified $1 refers to latest match at any given time, when a new match
      // is executed, the new $1 is in scope.
      // To avoid scope issues, we can refer to specific matches (book.$1, genre.$1).

      if( $1 =~ /genre='(.*?)'/ as genre ) {
         io.println("outer match: " + book.$1);
         io.println("inner match: " + genre.$1);
         io.println("inner match: " + $1);
      }
   }

   
				

.NET Tetris written in Cola

The usual Object Oriented features such as inheritance, encapsulation, polymorphism, aggregation, constructors. Pretty much any OO aspect of C# is included in Cola. Cola doesn't deviate from the successful C# / CTS design, and you can easily round trip code from Cola to MSIL to C#. Some immediate differences you may notice is the lack of per-member scope access control. Cola provides two alternate syntaxes for that.

	
// tetris.cola
//
// .NET Tetris - Full featured "Textris" game with scoring
//
// Demonstrates use of the .NET Console and ConsoleKey classes
// as well as various OOP, arrays, constructor delegation features
//
using System;

class GameBoard
{
     Point grid[22][50]; // extra room in array for move checking without worrying about array bounds
     int score;
     int level;
     int rowsCompleted;

     new() 
     {
          Console.SetWindowSize(80,42);
          score = 0;
          level = 1;
          // Initialize the board / grid with a border of fixed blocks
          for(int x = 0; x < 14; x++) {
               var border = new Point(x, 20, PointType.Permanent);
               border.Draw(this);
               border = new Point(x, 21, PointType.Invis);
               border.Draw(this);
          }

          for(int y = 0; y < 21; y++) {
               var border = new Point(0, y, PointType.Invis);
               border.Draw(this);
               border = new Point(1, y, PointType.Permanent);
               border.Draw(this);
               border = new Point(12, y, PointType.Permanent);
               border.Draw(this);
               border = new Point(13, y, PointType.Invis);
               border.Draw(this);
          }

          AddScore(0); // draw score
          AddNotice();
     }

     void Clear()
     {
          for(int row = 1; row < 20; row++) {
               DeleteRow(row);
          }
     }

     int CheckRows(bool countOnly)
     {
          int count = 0;
          bool gap = false;
          for(int row = 1; row < 20; row++) {
               gap = false;
               // Check this row
               for(int col = 2; col < 12; col++) {
                    if(grid[col][row] == null)
                         gap = true;
               }

               // If no gap, row is solid
               if(!gap) {
                    ++count;
                    if(countOnly)
                         continue;
                    ++rowsCompleted;
                    level = rowsCompleted / 10 + 1;
                    DeleteRow(row);
                    if(row > 1) {
                         for(int rowAbove = row-1; rowAbove >= 1; rowAbove--)
                              DropRow(rowAbove);
                    }
               }
          }
          return count;
     }

     bool IsCoordOpen(int x, int y)
     {
          if(grid[x][y] == null)
               return true;
          return false;
     }

     void DeleteRow(int row)
     {
          for(int col = 2; col < 12; col++) {
               if(grid[col][row] != null)
                    grid[col][row].Erase(this);
               grid[col][row] = null;
          }
     }

     void FlashRow(int row)
     {
          for(int col = 2; col < 12; col++) {
               if(grid[col][row] != null)
                    grid[col][row].SetColor(System.ConsoleColor.Yellow);
          }
     }

     // Move the row one down
     void DropRow(int row)
     {
          for(int col = 2; col < 12; col++) {
               if(grid[col][row] != null)
                    grid[col][row].Move(this, 0, 1);
          }
     }

     void AddScore(int i) 
     {
          score += i;
          console.gotoxy(40,3);
          io.print("Score: " + score + "   ");
          console.gotoxy(40,5);
          io.print("Level: " + level + "   ");
          console.gotoxy(40,6);
          io.print("Rows:  " + rowsCompleted + "   ");
     }

     void AddNotice()
     {
          console.gotoxy(30,13);
          io.print("Tetris written in Cola! (Enhancements welcome)");
          console.gotoxy(30,14);
          io.print("    For Katie and Chloe");
          console.gotoxy(30,16);
          io.print("    By: Daddy (Melvin Smith)");
          console.gotoxy(30,19);
          io.print("http://www.cola-lang.org");
          console.gotoxy(30,23);
          io.print("Space - Drop, Arrow Keys - Movement and Rotate");
     }

     void FlashRows()
     {
          bool gap;
          for(int row = 1; row < 20; row++) {
               if(RowIsSolid(row))
                    FlashRow(row);
          }
     }

     bool RowIsSolid(int row)
     {
          for(int col = 2; col < 12; col++) {
               if(grid[col][row] == null)
                    return false;
          }
          return true;
     }

     void Message(string mesg, ConsoleColor color) 
     {
          // Tetris bonus
          console.gotoxy(30,9);
          Console.BackgroundColor = color;
          Console.ForegroundColor = ConsoleColor.Black;
          io.print(mesg);
          Console.ResetColor();
     }
}

// Different ways to draw a block
enum PointType
{
     Permanent, // border
     Shape,  // live piece
     Invis,
     Dead // old pieces
}

// A single coordinate / block
class Point
{
     int x, y;
     PointType type;
     System.ConsoleColor color;

     new(x, y, type) {}
     new(x, y, type, color) {}

     void Draw(GameBoard board) 
     {
          string s;

          if(type == PointType.Permanent)
               s = "HH";
          else if(type == PointType.Invis)
               s = "  ";
          else if(type == PointType.Shape)
               s = "  ";
          else
               s = "  ";

          console.gotoxy(x*2,y*2);
          Console.BackgroundColor = color;

          io.print(s);
          console.gotoxy(x*2,y*2 + 1);
          io.print(s);

          Console.ResetColor();
          board.grid[x][y] = this; // add to grid
     }

     void Erase(GameBoard board) 
     {
          console.gotoxy(x*2,y*2);
          io.print("  ");
          console.gotoxy(x*2,y*2 + 1);
          io.print("  ");
          board.grid[x][y] = null; // remove from grid
     }

     void SetColor(System.ConsoleColor c)
     {
          color = c;
     }

     bool CanMove(GameBoard board, int xmove, int ymove) {
          // Is the point occupied or not?
          if(board.grid[x + xmove][y + ymove] == null 
               || board.grid[x + xmove][y + ymove].type == PointType.Shape)
               return true;
          return false;
     }

     void Move(GameBoard board, int xmove, int ymove) {
          Erase(board);
          x += xmove;
          y += ymove;
          Draw(board);
     }

     void Die(GameBoard board) {
          board.grid[x][y] = this; // ensure in grid
          type = PointType.Dead; // change to permanent
          Draw(board);
     }
}

class Shape
{
     GameBoard board;
     Point center;
     Point blocks[5];
     System.ConsoleColor color;
     int numBlocks;
     bool alive;
     //bool Alive { get; set; }

     new() 
     {
          alive = true;
          numBlocks = 0;
     }

     new(GameBoard board, int x, int y, System.ConsoleColor color) 
          : this()
     {
          numBlocks = 1;
          center = new Point(x, y, PointType.Shape, color);
          this.board = board;
          this.color = color;
     }

     void Draw() 
     {
          for(int i = 0; i < numBlocks; i++)
               blocks[i].Draw(board);
     }

     void Die() 
     {
          for(int i = 0; i < numBlocks; i++)
               blocks[i].Die(board);
          alive = false;
     }

     void Erase() 
     {
          for(int i = 0; i < numBlocks; i++)
               blocks[i].Erase(board);
     }

     bool CanMove(int xmove, int ymove) 
     {
          for(int i = 0; i < numBlocks; i++)
               if(blocks[i].CanMove(board, xmove, ymove) != true)
                    return false;
          return true;
     }

     // Move shape by x/y offset
     virtual void Move(int x, int y) 
     {
          //io.println("numBlocks: " + numBlocks);
          Erase();
          for(int i = 0; i < numBlocks; i++) {
               blocks[i].Move(board, x, y);
          }
          Draw();
     }
     
     void Drop() 
     {
          //Erase();
          while(CanMove(0,1)) {
               Move(0,1);
               os.sleep(1);
          }
          Draw();
     }
     
     // Standard rotate around a center
     virtual void Rotate() 
     {
          int i;
          int oldX;
          Erase();

          for(i = 1; i < numBlocks; i++) {
               int tryX = center.x + center.y - blocks[i].y;
               int tryY = center.y + blocks[i].x - center.x;
               if(!board.IsCoordOpen(tryX, tryY)) {
                    Draw();
                    return;  
               }
          }

          for(i = 1; i < numBlocks; i++) {
               oldX = blocks[i].x;
               blocks[i].x = center.x + center.y - blocks[i].y;
               blocks[i].y = center.y + oldX - center.x;
          }

          Draw();
     }

}

class Tee : Shape
{
      new(GameBoard board, int x, int y)
            : base(board, x, y, ConsoleColor.Cyan)
      {
            //center = new Point(x, y);
            numBlocks = 4;
            blocks[0] = center;
            blocks[1] = new Point(x + 1, y, PointType.Shape, this.color);
            blocks[2] = new Point(x - 1, y, PointType.Shape, this.color);
            blocks[3] = new Point(x, y + 1, PointType.Shape, this.color);
      }
}

class LongLine : Shape
{
      new(GameBoard board, int x, int y)
            : base(board, x, y, ConsoleColor.Red)
      {
            //center = new Point(x, y);
            numBlocks = 4;
            blocks[0] = center;
            blocks[1] = new Point(x + 1, y, PointType.Shape, this.color);
            blocks[2] = new Point(x - 1, y, PointType.Shape, this.color);
            blocks[3] = new Point(x - 2, y, PointType.Shape, this.color);
      }
}

class LeftAngle : Shape
{
      new(GameBoard board, int x, int y)
            : base(board, x, y, ConsoleColor.Blue)
      {
            //center = new Point(x, y);
            numBlocks = 4;
            blocks[0] = center;
            blocks[1] = new Point(x + 1, y, PointType.Shape, this.color);
            blocks[2] = new Point(x - 1, y, PointType.Shape, this.color);
            blocks[3] = new Point(x - 1, y - 1, PointType.Shape, this.color);
      }
}

class RightAngle : Shape
{
      new(GameBoard board, int x, int y)
            : base(board, x, y, ConsoleColor.White)
      {
            //center = new Point(x, y);
            numBlocks = 4;
            blocks[0] = center;
            blocks[1] = new Point(x + 1, y, PointType.Shape, this.color);
            blocks[2] = new Point(x - 1, y, PointType.Shape, this.color);
            blocks[3] = new Point(x + 1, y - 1, PointType.Shape, this.color);
      }
}

class Zig : Shape
{
   int position;

      new(GameBoard board, int x, int y)
            : base(board, x, y, ConsoleColor.Green)
      {
            position = 0;
            numBlocks = 4;
            blocks[0] = center;
            blocks[1] = new Point(x - 1, y, PointType.Shape, this.color);
            blocks[2] = new Point(x, y + 1, PointType.Shape, this.color);
            blocks[3] = new Point(x + 1, y + 1, PointType.Shape, this.color);
      }
}

class Zag : Shape
{
   int position;

      new(GameBoard board, int x, int y)
            : base(board, x, y, ConsoleColor.DarkMagenta)
      {
            position = 0;
            numBlocks = 4;
            blocks[0] = center;
            blocks[1] = new Point(x + 1, y, PointType.Shape, this.color);
            blocks[2] = new Point(x, y + 1, PointType.Shape, this.color);
            blocks[3] = new Point(x - 1, y + 1, PointType.Shape, this.color);
      }
}

class Square : Shape
{
   int position;

      new(GameBoard board, int x, int y)
            : base(board, x, y, ConsoleColor.Yellow)
      {
            position = 0;
            numBlocks = 4;
            blocks[0] = center;
            blocks[1] = new Point(x + 1, y, PointType.Shape, this.color);
            blocks[2] = new Point(x, y + 1, PointType.Shape, this.color);
            blocks[3] = new Point(x + 1, y + 1, PointType.Shape, this.color);
      }

      virtual void Rotate()
      {
          Erase();
          if(position == 0 || position == 3) {
               if((blocks[0].CanMove(board, 1, 0)) && (blocks[1].CanMove(board, 1, 0)))
               {
                    blocks[0].Move(board, 1, 0);
                    blocks[1].Move(board, 1, 0);
                    position = (position + 1) % 4;
               }
          } else if(position == 1 || position == 2) {
               if((blocks[0].CanMove(board, -1, 0)) &&(blocks[1].CanMove(board, -1, 0)))
               {
                    blocks[0].Move(board, -1, 0);
                    blocks[1].Move(board, -1, 0);
                    position = (position + 1) % 4;
               }
          }
          Draw();
      }
}


main()
{
     Random random = new Random();
     console.clrscr();
     console.cursor_off();

     GameBoard board = new GameBoard();

     ConsoleKey ch;
     int xWellStart = 5,
          yWellStart = 2;
     int xDeckStart = 16,
          yDeckStart = 2;

     int PULSE = 1000;
     int sleepTime = 0;
     bool paused = false;

     Shape shape, onDeck;

     while(true) 
     {
          if(shape == null) {
               // at bottom, old shape becomes part of the landscape :)
               int oldLevel = board.level;
               int rowsCompleted = board.CheckRows(false);
               if(rowsCompleted == 1) {
                    board.AddScore(100 * board.level);
                    board.Message("(*) SINGLE (*)           ", ConsoleColor.White);
               }
               else if(rowsCompleted == 2) {
                    board.AddScore(300 * board.level);
                    board.Message("(**) DOUBLE (**)         ", ConsoleColor.Green);
               }
               else if(rowsCompleted == 3) {
                    board.AddScore(500 * board.level);
                    board.Message("(***) TRIPLE (***)       ", ConsoleColor.Magenta);
               }
               else if(rowsCompleted == 4) {
                    board.AddScore(800 * board.level);
                    board.Message("(****( T E T R I S )****)", ConsoleColor.Red);
               }
               if(board.level > oldLevel)
                    PULSE = PULSE * 0.80;
               // Batter up
               shape = onDeck;
               if(shape != null) {
                    shape.Move(-10, 0);
               }
               // make a new shape
               int piece = random.Next(0, 7);
               switch(piece) {
                    case 0:
                              onDeck = new Tee(board, xDeckStart, yDeckStart);
                    case 1:
                              onDeck = new LongLine(board, xDeckStart, yDeckStart);
                    case 2:
                              onDeck = new Square(board, xDeckStart, yDeckStart);
                    case 3:
                              onDeck = new Zig(board, xDeckStart, yDeckStart);
                    case 4:
                              onDeck = new Zag(board, xDeckStart, yDeckStart);
                    case 5:
                              onDeck = new LeftAngle(board, xDeckStart, yDeckStart);
                    case 6:
                              onDeck = new RightAngle(board, xDeckStart, yDeckStart);
                    default:
                              println("Unknown shape " + piece);
               }

               onDeck.Draw();
               if(shape == null)
                    continue;

               if(!shape.CanMove(0,1)) {
                    board.Message("   .oO  GAME OVER  Oo.    ", ConsoleColor.Gray);          
                    io.getch();
                    console.cursor_on();
                    exit();
               }
          }

          if(io.peekch()) {
               ch = io.getkey();
               if(ch == ConsoleKey.Escape) {
                    paused = !paused;

                    if(paused) {
                         board.Message("       -- PAUSED --        ", ConsoleColor.White);
                         continue;
                    }
                    else {
                         board.Message("                           ", ConsoleColor.Black);
                    }
               }

               if(ch == ConsoleKey.UpArrow)
                    shape.Rotate();
               else if(ch == ConsoleKey.LeftArrow) {
                    if(shape.CanMove(-1, 0))
                         shape.Move(-1, 0);
               }
               else if(ch == ConsoleKey.RightArrow) {
                    if(shape.CanMove(1, 0))
                         shape.Move(1, 0);
               }
               else if(ch == ConsoleKey.DownArrow) {
                    if(shape.CanMove(0, 1))
                         shape.Move(0, 1);
               }
               else if(ch == ConsoleKey.Spacebar) {
                    shape.Drop();
                    shape.Die();
                    shape = null;
                    board.AddScore(board.level*5);
               }
          }

          os.sleep(25);
          sleepTime += 25;
 
          if(paused)
               continue;

          if(shape != null) {
               if(sleepTime > PULSE) {
                    if(shape.CanMove(0,1))
                         shape.Move(0, 1);
                    else {
                         shape.Die();
                         shape = null;
                    }
                    sleepTime = 0;
               }
          } 
     }
}

/*
   Donators and Contributers:

   Melvin Smith - Original console Tetris and Cola compiler + $10

  
*/

Tetris Screenshot

This is the third Tetris I've written in my life, but it was the most fun. This is a retro text game that reminds me how fun programming can be. I joke that it was the most difficult Tetris to write, considering I had to write a compiler first. Check it out in the samples directory and feel free to improve on it. I plan to add a OpenGL / XNA version shortly.

Tetris in Cola

Generics

I use a CTS compatible naming convention like Microsoft C# and VB.NET compilers. I have not yet begun testing nested generics but wrote the code with that in mind. I am very proud of this feature, it is something I have been planning for several years.

	
//
// First working sample of generics / containers
// Also demonstrates default(T), nested array of T and generic constructor

   class Stack<T>
   {
      // fields
      int   @top;
      T     @vector[100];
 
      // properties
      int   Length { get { return top; } set { top = value; } }

      T Top
      {
         get { if (top > 0) return vector[top - 1]; return default(T); }
      }

      T Bottom
      { 
         get { if (top > 0) return vector[0]; return default(T); }
      }

      // constructor
      new() {
         top = 0;
      }

      // methods
      void Push(T item)
      { 
         vector[top++] = item;
      }

      T Pop()
      {
         T t = Front();
         if(top > 0)
            top--;
         return t;
      }

      T Front()
      {
         if(top > 0)
            return vector[top -1 ];

         return default(T);
      }
   }

   main()
   {
      var stack = new Stack();

      stack.Push("A");
      stack.Push("B");
      stack.Push("C");

      io.println("Stack has " + stack.Length + " items");
      io.println("Top item is " + stack.Top);
      io.println("Bottom item is " + stack.Bottom);

      io.println(stack.Pop());
      io.println(stack.Pop());
      io.println(stack.Pop());

      io.println("Stack has " + stack.Length + " items");
   }

Use the .NET Framework

Cola currently targets the .NET CLR runtime, so it has access to all of the .NET framework, from Winforms, ADO.NET, etc. The support is still basic, but is coming along. 0.24 adds the "using" directive to import namespaces into the current namespace. On the TODO list are events, delegates and lambdas as well as custom attributes. Cola.NET requires the .NET framework or Mono.


	
// .NET System.Xml Example
using System.Xml;

main() {
   var doc = new XmlDocument();
   doc.LoadXml(
       @"<book genre='novel' ISBN='1-861001-57-5' misc='sale item'&rt
         <itle&rtThe Handmaid's Tale</title&rt
                <price&rt14.95</price&rt
                </book&rt" );

   var element = doc.DocumentElement;
   io.println(element.InnerXml);
}


using System.Windows.Forms;

main() {
   var form = new Form();
   form.Text = "Cola Winforms Sample";
   MessageBox.Show("Testing", "Testing", MessageBoxButtons.YesNoCancel);
   
   Application.Run(form);
}



// ADO.NET Oracle sample
//
//   colac ado.cola -r Oracle.DataAccess
//
main() {
   string ename, job;
   var conn = new Oracle.DataAccess.Client.OracleConnection(
                        "Data Source=DEV;User Id=scott;Password=tiger;");
   conn.Open();
   var cmd = new Oracle.DataAccess.Client.OracleCommand("SELECT ENAME, JOB FROM EMP", conn);
   var reader = cmd.ExecuteReader();
   while(reader.Read()) {
      ename = reader.GetString(0);
      job = reader.GetString(1);
      // All names that start with S
      if(ename =~ /^S/)
         io.println("Employee: " + ename + ", Job: " + job);
   }
   conn.Close();
}

Array/List Syntax

Cola supports N-dimensional dynamic and auto arrays with dual declaration syntax options. You can use C/C++ style postfix notation (int arr[10]) or C#/Java-like prefix notation (int[10] arr). When Cola sees a postfix declaration it automatically allocates storage for the array. Pre-fix "Java" arrays work like Java arrays. You can use curly braces { } for inline array literals in loops or arguments, or as static initializers.

	
main()
{
   int small[] = {1, 2, 3};

   string words[] = {
      "This", "is", "a", "statically", "initialized", "dynamic", "array", "!"
    };

   int big[] = {
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
      11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
      21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
      31, 32, 33, 34, 35, 36, 37, 38, 39, 30,
      41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
      51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
      61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
      71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
      81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
      91, 92, 93, 94, 95, 96, 97, 98, 99, 100
   };

   for(int j = 0; j < big.Length; j++)
      io.println(big[j]);

   for(int word = 0; word < words.Length; word++)
      io.print(words[word] + " ");
   io.println("");

   for(int i = 0; i < small.Length; i++)
      io.println(small[i]);

   for(var item in {1,2,3,4,5,5,4,3,2,1}) {
      io.println(item);
   }

   for(var s in {"this","is", "a", "inline", "array", "!"}) {
      io.println(s);
   }
   
   // N-Dimensional Auto-allocated Arrays 
   
   string board[3][3];  // auto
	
   board[0][0] = "X";
   board[0][1] = "O";
   board[0][2] = "X";
   board[1][0] = "O";
   board[1][1] = "X";
   board[1][2] = "O";
   board[2][0] = "X";
   board[2][1] = "O";
   board[2][2] = "X";

   io.println("Tic Tac Toe: ");

   for(int x = 0; x < 3; x++) {
      for(int y = 0; y < 3; y++)
         io.print(board[x][y]);
      io.println("");
   }
}

.NET Framework Metadata Update for 0.23 - Generics and Properties Added

0.23 gives some attention to the .NET framework integration. colastub.exe now generates C# style properties and generics now that Cola supports them. The core framework prototype files were also updated. If you have a different version of the framework you can move the files aside and compile a program to generate new proto files. Be advised, colastub isn't perfect; you may have to edit files by hand to fix. This is work in progress. Any takers? :)

	
// .NET System.Diagnostics.Process.Start sample to execute an OS command
main() {
   var s = system("colac.exe", "tetris.cola");
   io.println(s);
}

// Cola provides this as os.system()
string system(string cmd, string args)
{
   var p = new System.Diagnostics.Process();
   
   p.StartInfo.FileName = cmd;
   p.StartInfo.Arguments = args;
   p.StartInfo.UseShellExecute = false;
   p.StartInfo.RedirectStandardOutput = true;   
   p.StartInfo.RedirectStandardError = true;   
   p.Start();
   string ret = p.StandardError.ReadToEnd();  // read child process output
   p.WaitForExit();
   return ret;
}

Constructor Delegation

Simply name your constructors new(). I may add traditional C#/C++/Java syntax for compatibility / habit, but I prefer to use new() for clarity and brevity. Call base class with base() or delegate to another constructor with this(). If you name the parameter after a field and leave off the type, Cola will bind it by name and initialize the field immediately. This allows you to skip writing assignment statements and encourages RAII practices.

		  
class A
{
   int x, y;

   new()
   {
      io.println( "A::new()");
   }

   new(x, y)
      // : this(1,2,3) uncomment this to test dependency loops once compiler detects them
   {
	   // this.x and this.y are initialized by name
      io.println( "A::new(x,y): x: " + x + " y: " + y);
   }

   new(int argx, int argy, int argz)
      : this(argx,argy)
   {
      io.println( "A::new(int argx, int argy, int argz): argz: " + argx + " argy: " + argy + " argz: " + argz);
   }
}

class B : A
{
   new() : base(1,2,3)
   {
      io.println( "B::new()");
   }
};
	

Member Initialization Syntax

I am supporting both C# and Javascript syntax for object and container construction / initialization, so the equal sign and the colon are interchangeable within the member initializers. Due to some trouble with the parser, I had to leave out the empty parentheses as an option when using member initializers, so it is "new Foo { ... }" but not "new Foo() { .. }" until I debug the grammar. Parens are only allowed when using a standard constructor. This is the suggested syntax for writing the same thing in C# anyway, but C# does handle the optional parens.

	
// Member initialization syntax

class Person
{
   string  Name;
   int     Age;
   int     Items[];
   string  Description;
}

main()
{   
   var p = new Person {
      Items: {1,2,3,4,5},
      Name: "Melvin",
      Age:  41,
      Description = "This is a description...."   // equals sign also works
   };

   io.println(p.Name);
   io.println(p.Age);
   io.println(p.Description);
   foreach(var item in p.Items) {
      io.println(item);
   }
}



C++ Style Scope Access = Less Clutter

The public keyword isn't supported as part of an individual declaration per Java/C#, but I have added C++ style blocks. I have always felt that this was the area in these languages where we were forced to repeat ourselves ceremoniously over and over, and it obscures the fields and methods to the point where our brains "hide" those keywords from us. You can instead use @ for private fields if you want per-field scope access. I am still experimenting with alternatives and I am open to ideas.

	
class Foo
{
private:
   int x;

public:
   new(x)
   {
      io.println( "constructor: this.x initialized to: " + this.x);
   }
}

Getting Involved

Cola is still in prototype stage. I am building it to experiment with different constructs and features, and to finalize the grammar. You can currently mix Cola into a larger .NET project. Check the examples directory for the current supported features.

If you are interested in Cola, please download the compiler and look at the examples. If you don't have compiler building skills that's ok, there are tasks like implementing a test harness or writing test cases or standard library modules.

The original compiler is written in C++ with Bison; Eventually I plan to reimplement the parser by hand.