Thursday, February 25, 2010

Like, C#

Yesterday I read absolutely hilarious post on Like, Python (http://www.staringispolite.com/likepython/) - a valleygirl/hillbilly dialect of Python. It was a big hit at work, and people immediately started asking me about the C# version.

You know our motto - we deliver!

So after 30 minutes of stitching together pieces from Malevich's Syntax Highlighter (to allow proper treatment of comments and string constants) (http://malevich.codeplex.com), and Scriptster (http://scriptster.codeplex.com), I present you with Like, C#.

Like, C# uses the same extended set of "keywords" as Like, Python, so we can write this code:

ohai

totally using System man;

uh class like Program
{
ok static void Main(string[] args) bro
{
just Console.Write("yo! what's your name?");
ok so like string name = Console.ReadLine();

if actually (name.Equals(""))
omg toootally just return;

um yeah

plz Console.WriteLine(like "Hi {0}, nice to meet you!", name);
}
}


Full list of "keywords" is here:

  • Valleygirl: omg, so, like, totally, right, toootally

  • Frat guy: friggin, fuckin, dude, man, bro, broheim, broseph

  • Internets: lol, rofl, teh, ohai, plz

  • Snoop: yo, homey, homeboy, sup, dog, shit, girl, ma, biatch, ho, shiiit

  • Local: wicked, hella, anyways

  • Misc: just, hey, yeah, ok, um, uh, ah, actually, something



And here's the full source of Like, C# compiler if you want to adopt this new, rapidly growing language:

//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Sergey Solyanik">
// Copyright (C) Sergey Solyanik.
//
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace LikeCSharp
{
/// <summary>
/// Implements Like, C# compiler.
/// </summary>
class Program
{
/// <summary>
/// Extension keywords.
/// </summary>
private static string[] keywords =
{
"omg", "so", "like", "totally", "right", "toootally",
"friggin", "fuckin", "dude", "man", "bro", "broheim",
"broseph", "lol", "rofl", "teh", "ohai", "plz",
"yo", "homey", "homeboy", "sup", "dog", "shit", "girl",
"ma", "biatch", "ho", "shiiit", "wicked", "hella", "anyways",
"just", "hey", "yeah", "ok", "um", "uh", "ah", "actually",
"something"
};

/// <summary>
/// Main entry point.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
string runtimeEnvironmentPath =
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
if (runtimeEnvironmentPath.EndsWith("\\"))
{
runtimeEnvironmentPath = runtimeEnvironmentPath.Substring(
0, runtimeEnvironmentPath.Length - 1);
}

string compilerExe = Path.Combine(runtimeEnvironmentPath, "csc.exe");

// However, if it's 2.0, there is a compiler-only extension (3.5).
// If 3.5 is indeed installed, use that.
string baseFrameworkPath = Path.GetDirectoryName(runtimeEnvironmentPath);
string runtimeName = Path.GetFileName(runtimeEnvironmentPath);
if ("v2.0.50727".Equals(runtimeName))
{
string compilerExe35 = Path.Combine(
Path.Combine(baseFrameworkPath, "v3.5"), "csc.exe");
if (File.Exists(compilerExe35))
{
compilerExe = compilerExe35;
}
}

StringBuilder sb = new StringBuilder();
bool firstArg = true;
HashSet<string> files = new HashSet<string>();

string keywordRE = "\"|'|//|/\\*|#?[a-z_0-9]+";
HashSet<string> keywordDictionary = new HashSet<string>(keywords);
Regex keywordRegex = new Regex(
keywordRE, RegexOptions.Compiled | RegexOptions.IgnoreCase);

foreach (string arg in args)
{
if (firstArg)
{
firstArg = false;
}
else
{
sb.Append(' ');
}

string newArg = arg;
if (arg.EndsWith(".lcs"))
{
newArg = Preprocess(arg, keywordRegex, keywordDictionary);
files.Add(newArg);
}

if (newArg.Contains(' '))
{
newArg = '"' + newArg + '"';
}

sb.Append(newArg);
}

Process compiler = new Process();
compiler.StartInfo.UseShellExecute = false;
compiler.StartInfo.FileName = compilerExe;
compiler.StartInfo.Arguments = sb.ToString();

compiler.Start();
compiler.WaitForExit();

Environment.ExitCode = compiler.ExitCode;

foreach (string file in files)
{
File.Delete(file);
}
}

/// <summary>
/// Converts file from Like, C# to normal C#.
/// </summary>
/// <param name="fileName"> File name to process. </param>
/// <param name="keywordRegex"> Regular expression that
/// matches language elements. </param>
/// <param name="keywordDictionary"> Like keywords. </param>
/// <returns> Name of the temporary file. </returns>
static string Preprocess(
string fileName,
Regex keywordRegex,
HashSet<string> keywordDictionary)
{
string tempDir = Environment.GetEnvironmentVariable("TEMP");
tempDir = Path.Combine(tempDir, "LIKECSHARP");
if (!Directory.Exists(tempDir))
{
Directory.CreateDirectory(tempDir);
}

string target = Path.Combine(
tempDir,
Path.GetFileNameWithoutExtension(fileName) + ".cs");

QuoteType quotes = QuoteType.None;
bool inComments = false;

StreamReader reader = new StreamReader(fileName);
StreamWriter writer = new StreamWriter(target);

for (; ; )
{
string s = reader.ReadLine();
if (s == null)
{
break;
}

StringBuilder encoded = new StringBuilder();

int i = 0;
while (i < s.Length)
{
if (quotes == QuoteType.Double)
{
int endQuotes = s.IndexOf("\"", i);
if (endQuotes == -1)
{
// The entire string is in quotes.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

encoded.Append(s.Substring(i, endQuotes + 1 - i));
if ((endQuotes > 0 && s[endQuotes - 1] != '\\') ||
(endQuotes > 1 && s[endQuotes - 1] == '\\' &&
s[endQuotes - 2] == '\\'))
{
// This is not an escaped quote.
quotes = QuoteType.None;
}

i = endQuotes + 1;
continue;
}

if (quotes == QuoteType.Verbatim)
{
int endQuotes = s.IndexOf("\"", i);
if (endQuotes == -1)
{
// The entire string is in quotes.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

if (s.IndexOf("\"\"", endQuotes) == endQuotes)
{
// This is an escaped quote.
encoded.Append(s.Substring(i, endQuotes + 2 - i));
i = endQuotes + 2;
continue;
}

encoded.Append(s.Substring(i, endQuotes + 1 - i));
quotes = QuoteType.None;

i = endQuotes + 1;
continue;
}

if (quotes == QuoteType.Single)
{
int endQuotes = s.IndexOf('\'', i);
if (endQuotes == -1)
{
// The entire string is in quotes.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

encoded.Append(s.Substring(i, endQuotes + 1 - i));
if ((endQuotes > 0 && s[endQuotes - 1] != '\\') ||
(endQuotes > 1 && s[endQuotes - 1] == '\\' &&
s[endQuotes - 2] == '\\'))
{
// This is not an escaped quote.
quotes = QuoteType.None;
}

i = endQuotes + 1;
continue;
}

if (inComments)
{
int endComments = s.IndexOf("*/", i);
if (endComments == -1)
{
// The entire string is in comments.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

inComments = false;

encoded.Append(
s.Substring(i, endComments + 2 - i));
i = endComments + 2;
continue;
}

Match nextMatch = keywordRegex.Match(s, i);
if (!nextMatch.Success)
{
encoded.Append(s, i, s.Length - i);
break;
}

if (i != nextMatch.Index)
{
encoded.Append(s, i, nextMatch.Index - i);
i = nextMatch.Index;
}

string matched = nextMatch.Groups[0].Value;

if (matched.Equals("'"))
{
encoded.Append("'");
quotes = QuoteType.Single;
++i;
continue;
}

if (matched.Equals("\""))
{
encoded.Append("\"");
quotes = QuoteType.Double;
++i;
continue;
}

if (matched.Equals("@\""))
{
encoded.Append("@\"");
quotes = QuoteType.Verbatim;
i += 2;
continue;
}

if (matched.Equals("//"))
{
// The rest of the line is comments.
encoded.Append(s.Substring(i));
break;
}

if (matched.Equals("/*"))
{
// Comments start.
encoded.Append("/*");
inComments = true;
i += 2;
continue;
}

if (keywordDictionary.Contains(matched))
{
// Keyword. Eat it.
i += matched.Length;
}
else
{
encoded.Append(matched);
i += matched.Length;
}
}

writer.WriteLine(encoded.ToString());
}

reader.Close();
writer.Close();

return target;
}

/// <summary>
/// Types of quoted string.
/// </summary>
enum QuoteType
{
None,
Single,
Double,
Verbatim
}
}
}