Sunday, August 30, 2015

LiveCode Builder without the LiveCode bit

Since my last post almost two years ago, I've moved to Edinburgh. I now work for LiveCode as an open source software engineer.

Introducing LiveCode Builder

LiveCode 8, the upcoming release of the LiveCode HyperCard-like application development environment, introduces a new xTalk-like language for writing LiveCode extensions. It's called LiveCode Builder (or LCB). It shares much of the same syntax as the original LiveCode scripting language, but it's a compiled, strongly-typed language.

Most of the public discussion about LiveCode Builder has revolved around using it to extend LiveCode — either by creating new widgets to display in the user interface, or by writing libraries that add new capabilities to the scripting language. However, one topic that *hasn't* been discussed much is the fact that you can write complete applications using only LCB, and compile and run them without using the main LiveCode engine at all.

LiveCode Builder without the engine

This is actually pretty useful when writing simple command-line tools or services that don't need a user interface and for which the main LiveCode engine provides little value (for example, if you need your tool to start up really quickly). There are a couple of good examples that I've written during the last few months.

The LCB standard library's test suite uses a test runner written in LCB. This is quite a useful "smoke test" for the compiler, virtual machine, and standard library -- if any of them break, the test suite won't run at all!

More recently, I've written a bot that connects our GitHub repositories to our BuildBot continuous integration system. Every few minutes, it checks the status of all the outstanding pull requests, and either submits new build jobs or reports on completed ones. This is also written entirely in LCB. One of main advantages of using LCB for this were that LCB has a proper List type that can contain arrays as elements.

"Hello World" in LCB

A pure LCB program looks like this:

module org.example.helloworld

public handler Main()
   write the contents of file "hello.txt" to the output stream
end handler

end module

It has a top-level module, that contains a public handler called Main. Note that unlike in C or C++, the Main handler doesn't take any arguments (you can access the command-line arguments using `the command arguments`).

Next, you need to compile your application using the lc-compile tool. To do this, you need to locate the directory from the LiveCode installation that contains the `.lci` files -- these are LiveCode's equivalent to C or C++'s header files. For example, on my system, I could compile the example above using (let's assume I've saved it to a file called hello.lcb:

$ export TOOLCHAIN='/opt/runrev/livecodecommunity-8.0.0-dp-3 (x86_64)/Toolchain/
$ "$TOOLCHAIN/lc-compile" --modulepath . --modulepath "$TOOLCHAIN/modules/lci" --output hello.lcm hello.lcb

These commands generate two files: hello.lcm, containing LCB bytecode, and org.example.helloworld.lci containing the interface.

Finally, you can run the program using lc-run. This is a really minimal tool that provides only the LCB virtual machine and standard library.

$ echo "Hello world!" > hello.txt
$ "$TOOLCHAIN/lc-run" hello.lcm
Hello world!

Finding out more

To more information on the standard library syntax available in LCB, visit the "LiveCode Builder" section of the dictionary in the LiveCode IDE. Note that the "widget", "engine" and "canvas" syntax isn't currently available to pure LCB programs. You should also check out the "Extending LiveCode" guide.


Unknown said...

Why did you require a text file rather than just write hello world to stdout?
Seems like an extra step when hello world demos are usually the simplest possible form.

Peter Brett said...

Currently, the LCB standard library is pretty incomplete. One manifestation of its incompleteness is that you can write a Data to a stream, but you can't write a String to a stream (because writing a String would require some non-obvious semantics about how to choose the correct string encoding).

You can currently get around this by calling a C function using the LCB foreign function interface. However, I thought that it might be a little bit too in-depth for a introductory post! The LCB standard library test runner has an example of how to encode a String to Data.

fortyfoxes said...

Can you create standard "simple command-line tools" or do you have to use the lc-run programme as in this example?

Peter Brett said...

LiveCode Builder source code currently compiles to a simple bytecode, which needs to be executed by a specialized bytecode interpreter (part of lc-run). It's similar in that respect to Java.

In the long term, ideally we would compile LiveCode Builder programs all the way to native code, and then you could run it directly.

For the time being, though, you will need to use lc-run to run your compiled LCB bytecode (just like you use java to run JVM bytecode).

Paul said...

Thanks for this incite into the what is/will be possible with LCB. I'm so looking forward to an truely-XTalk like language that has all the power of a traditional (cryptic) language like C, C++, ObjC, etc. It's currently looking kind of like a mashup of Hypertalk & Pascal!
Thanks for pointing me to the test-runner LCB source with it's use com.livecode.foreign. I'm really exited about it and want to learn more about the foreign function interface (not that widgets are a super-great and fun, but mostly it's stuff that could be done before with custom controls in LCS). Specificaly I'm looking for how to import/link to C library headers? Obj.C is the Mac OS X norm, but the one API I'm most interested in using from LC, CoreMIDI, is straight-up C! Theoretically it should already be accessible from LCB's foreign code interface?

Peter Brett said...

I'm glad you enjoyed this post, Paul! Check out my next post for a brief example of using the foreign function interface. If you look in the "Extending LiveCode" guide in the IDE, there's a more detailed (and technical) guide to accessing functions in external libraries. If CoreMidi is a C API you should already be able to access it — but depending on how the API is designed, you might not be able to do anything with it. There are still some missing features in LCB's FFI.

John Barness said...
This comment has been removed by a blog administrator.