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
}
}
}

3 comments:

Anonymous said...

Very funny!

DzembuGaijin said...

It became my language of choice now! :-)

If I could only get my hands on "Like ObjC" when I need to write code for iPhone!!!!

Andrei Rubaniuk said...

Sergey, this is hilarious!

Quickly reviewed it, following code from the beginning of Main() can be removed, Path.Combine will take care or '\\':

if (runtimeEnvironmentPath.EndsWith("\\"))
{
runtimeEnvironmentPath = runtimeEnvironmentPath.Substring(
0, runtimeEnvironmentPath.Length - 1);
}