Wednesday, June 25, 2008

AbcExplorationLib: Starting an open source project

A couple of months ago I started working on a F# library to read and write ActionScript Byte Code (ABC) files based on the ActionScript Virtual Machine 2 (AVM2) Overview document. I though this was a great opportunity to learn more about both F# and ActionScript.

Although the library is still pretty incomplete, I'll continue the development as an open source project. The project is called AbcExplorationLib and is hosted in CodePlex .

Inspiration for this library comes from excellent bytecode manipulation libraries such as BCEL, ASM, Cecil or System.Reflection.Emit.

When completed this library could be used as part of a complied code analysis tool or as part of the back end of a experimental compiler.

An example of using this library to load a compiled script and print all the names of the opcodes is the following:


let loadedFile =
using(new BinaryReader(new FileStream("Hello.abc",FileMode.Open)))
(fun aInput -> AbcFile.ReadFrom(aInput))


let loadedScript =
AvmScript.Create(loadedFile.Scripts.[0],
loadedFile.Methods,
loadedFile.MethodBodies,
loadedFile.ConstantPool);

Array.iter
(fun (x:AbcFileInstruction) -> printf "%s\n" x.Name)
(loadedScript.InitMethod.Body.Value.BodyInfo.GetInstructions())



An example for generating a "Hello world" program:


let abcFileCreator = AbcFileCreator()
let cpCreator = abcFileCreator.ConstantPoolCreator

let instructions =
[| GetLocal0 ;
PushScope ;
FindPropertyStrict(cpCreator.GetMultiname([|""|],"print"));
PushString(cpCreator.AddString( "Hola!"));
CallProperty(cpCreator.GetMultiname([|""|],"print"),1) ;
CoerceA ;
SetLocal_1 ;
GetLocal1 ;
ReturnValue ;
Kill(1);
|];

let code = ConvertToByteArray(instructions)

let script =
AvmScript(
AvmMethod( "",
SQualifiedName("*"),
[||],
Some (
AvmMethodBody(
AbcMethodBodyInfo(
0, 2, 2, 1, 2,
code,
[||],[||]
)))));

abcFileCreator.AddScript(script.ToLowerIr(abcFileCreator))

let fileRep = abcFileCreator.CreateFile()

using (new BinaryWriter(new FileStream("Hello.abc",FileMode.Create))) (fun f -> fileRep.WriteTo(f))


As you can see this is the part that requires more work! :) .

Future posts will cover the progress of this library.

Monday, June 9, 2008

Using F# option types in C#

This post shows a small example of how to use F# option type instances in C#.

Option types

The F# option type provides a very nice way for defining variables that can may or may not hold a value. For example, in the following class definition, the description field is optional .


type Product(name : string, description : string option) =
class

member this.Name
with get() = name

member this.Description
with get() = description

end


F# Optional types are constructed using the Some and None constructors. For example:


let p1 = Product("Foo",Some "A description")
let p2 = Product("Goo",None)


Optional types are available in many functional languages, for example: Haskell's Maybe, Scala's Option, OCaml's option, among others.

Using option types in C#

Manipulating a option instance from C# requires some considerations since it is defined in F# as a discriminated union. Some useful documentation on how to manipulate discriminated unions from C# includes: Creating and Consuming F# discriminated unions from F#'s manual and this entry from the hubFS.

An instance of the class defined above can be created in C# like this:


Product p = new Product("Foo", Option<string>.Some("A description"));
Product p2 = new Product("Goo", Option<string>.None);


As shown here the Option<A> class (defined in Microsoft.FSharp.Core) provide static methods to construct both cases of the discriminated union.

Several techniques can be used to verify if an instance of Option<A> was created with Some or None. For example, there are static methods called get_IsNone or get_Some which can be used as follows:


if (Option<string>.get_IsNone(p.Description))
{
...
}
...
if (Option<string>.get_IsSome(p.Description))
{
...
}


Another alternative is to use the GetTag method as follows:


if(Option<string>.GetTag(p.Description) == Option<string>.tag_Some) {
...
}


Extension methods

C# extension methods can be used to avoid calling static methods on the Option<A> class. For example:


static class Utility
{
public static bool IsSome(this Option<string> option)
{
return Option<string>.get_IsSome(option);
}

public static bool IsNone(this Option<string> option)
{
return Option<string>.get_IsNone(option);
}
}


Having these extension methods the code could be changed to:



if (p.Description.IsSome())
{
...
}

if (p2.Description.IsSome())
{
...
}


C# extension methods could also be implemented directly in F# . This could be accomplished by applying the System.Runtime.CompilerServices.ExtensionAttribute attribute. For example:


[<System.Runtime.CompilerServices.Extension>]
module Methods =

[<System.Runtime.CompilerServices.Extension>]
let Exists(opt : string option) =
match opt with
| Some _ -> true
| None -> false


This method could be used in C# only by adding the namespace (using using) to the file where it will be used.


if (p2.Description.Exists())
{
...
}

Sunday, June 1, 2008

Creating a simple AIR/Flex UI for a Snobol program

This post presents a little experiment for communicating an Snobol program with an AIR/Flex interface using HTTP.

This post is inspired by the Put a Flex UI On Your Application article by Bruce Eckel.

The example

The example that will be presented is a simple form showing sudo attempts recorded in the /var/log/auth.log log in a Linux box.

A simple program written in Snobol4 using CSnobol4 is used to extract the entries from auth.log . A AIR/Flex program is used to display the data. Both programs are communicated using HTTP.

Although there are several ways to communicate a AIR application with a server side element, HTTP was chosen because of its simplicity to implement in Snobol.

Simple HTTP in Snobol

A way to answer simple HTTP GET method requests from Snobol was required. This requires the creation of a server socket to answer requests. Luckly CSnobol4 includes a nice example for creating a server socket (snolib/serv.sno) using the SERV_LISTEN function. Having this element it was very simple to implement the GET method support.


serverPort = 8080
...
SLOOP FD = SERV_LISTEN("inet", "stream", serverPort) :F(LERR)

INPUT(.NET, 9, "UWT", "/dev/fd/" FD) :F(IERR)
OUTPUT(.NET, 9)

OUTPUT = "Accepting request "

LINE = NET

LINE "GET " GetStringPat $ getString " HTTP/" NUMBER "." NUMBER :F(LERR)

getString ARB "username=" ARB $ requestedUserName ("&" | RPOS(0))

OUTPUT = "Requesting SUDOS for user: " requestedUserName

INPUT(.LOGFILE,10,,"/var/log/auth.log")

NET = "HTTP/1.1 200 OK" CRLF
NET = "Server: SNOBOL4/1.1 (Linux)" CRLF
NET = "Content-Type: text/xml" CRLF
NET = CRLF


Extracting the data

The data extraction process is presented in the following code.


LetterU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LetterL = "abcdefghijklmnopqrstuvwxyz"
Digit = "0123456789"
DirectorySeparator = "/"
GetQuerySeparator = "?"
EscapeChar = "%.&="

GetStringChar = LetterU LetterL Digit DirectorySeparator EscapeChar GetQuerySeparator

GetStringPat = SPAN(GetStringChar)

...



NET = "<sudosdata>"

&ANCHOR = 1
LETTER = LetterU LetterL
USERNAMECHAR = Digit LetterU LetterL
USERNAMEPAT = SPAN(USERNAMECHAR)


READLINE LINE = LOGFILE :F(DONE)
LINE SPAN(LETTER) $ month SPAN(" ") SPAN(Digit) $ day ARB " sudo: " USERNAMEPAT . USER :F(READLINE)
LINE ARB "COMMAND=" ARB . COMMAND RPOS(0)

USER requestedUserName :F(READLINE)

NET = "<sudo><date>" month " " day "</date><user>" USER "</user><command>" COMMAND "</command></sudo>" :(READLINE)

DONE

NET = "</sudosdata>" :S(END)


The answer is formated as XML.

The Interface Code

The interface is a very simple program containing a text input to filter the query for a specify user. The useful HTTPService component is used to get the data from the Snobol program.

The Flex part of the program is the following:


<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
title="Sudos Test">
<mx:Script>
<![CDATA[
import flash.utils.Dictionary;
private function callQuery():void {
request.send();
}
]]>
</mx:Script>
<mx:HBox>
<mx:Label text="Sudos" />
<mx:TextInput id="userName" />
</mx:HBox>
<mx:Button label="Query!" click="callQuery()"/>
<mx:DataGrid id="data" dataProvider="{request.lastResult.sudosdata.sudo}">
<mx:columns>
<mx:DataGridColumn headerText="date" dataField="date"/>
<mx:DataGridColumn headerText="user" dataField="user"/>
<mx:DataGridColumn headerText="command" dataField="command"/>
</mx:columns>
</mx:DataGrid>

<mx:HTTPService id="request" url="http://localhost:8080"
useProxy="false"
method="GET">
<mx:request xmlns="">
<username>{userName.text}</username>
</mx:request>
</mx:HTTPService>
</mx:WindowedApplication>


How it looks

An example of running these programs is the following:



Code for this post can be found here.