Wednesday, November 04, 2015

Japanese Shioyaki-style mackerel

This is a guest post written by Kathryn Grant, who has a knack for picking out exotic yet easy-to-cook recipes!

This is a quick version of 鯖の塩焼き (saba no shioyaki), or salt-grilled mackerel, served with cucumber pickle and toasted sesame seeds. This recipe serves 2 people.


For the cucumber pickle:

  • ½ cucumber, halved lengthways and sliced
  • 1 tsp cooking salt
  • 50 ml rice wine (or white wine) vinegar
  • 3 tbsp dark soy sauce
  • 1 tbsp toasted sesame seed oil
  • 1 tsp sugar
  • ¼–½ tsp chilli powder
  • 3 spring onions

For the grilled mackerel:

  • 3 tbsp soy sauce
  • 1 tbsp rice wine (or white wine) vinegar
  • 1 tsp toasted sesame oil
  • 2 fresh mackerel fillets
  • Sea salt
  • Vegetable oil

For the rice:

  • 120-180 g rice (depending on hunger levels)
  • 1 tbsp toasted sesame oil
  • Sea salt

To serve:

  • 2 tbsp black sesame seeds
  • Lemon wedges
  • Finely sliced daikon radish or other radish (optional)


  1. Chop the cucumber and place in a bowl. Sprinkle 1 tsp cooking salt over the cucumber and leave for 5 minutes. Meanwhile, mix the marinade ingredients together: vinegar, soy sauce, toasted sesame oil, sugar and chilli powder. Chop the spring onion. Once the 5 minutes is up, rinse the cucumber thoroughly with cold water to remove the salt, drain and place back into the bowl. Pour over the marinade, add in the spring onions, cover with clingfilm and set aside somewhere cool.
  2. Mix the marinade for the mackerel: soy sauce, vinegar and toasted sesame oil. Pour into a shallow dish. Wash the fish and place, skin-side up, in the shallow dish. Leave to marinate for 10 minutes.
  3. Pre-heat the grill to a high heat.
  4. Toast the black sesame seeds in the bottom of a dry pan for around 2 minutes, taking care not to burn them. Remove from the heat and set aside.
  5. Shred the radish, if using.
  6. Boil a kettle of water. Heat 1 tbsp of toasted sesame oil in a saucepan. Wash the rice thoroughly, until the water runs clear, then add to the saucepan. Fry for 1 minute, stirring continuously to make sure the rice does not burn. Cover the rice with water, season with a pinch of salt and simmer for approximately 12 minutes (check packet instructions).
  7. Whilst the rice is cooking, remove the mackerel from the marinade and pat dry with paper towels to remove excess moisture. Sprinkle the non-skin side with sea salt and let the fish rest for 5 minutes.
  8. Prepare a tray for grilling: line a baking tray with foil and grease with about 1 tbsp of vegetable oil.
  9. After the fish has rested, place onto the baking tray (skin-side down) and grill for 5 minutes until the fish is cooked. The skin should be crispy and surface lightly browned.
  10. Serve the cucumber pickle and rice sprinkled with the toasted sesame seeds. The excess cucumber marinade makes an excellent sauce for the rice. Serve the fish with lemon (or lime) wedges and shredded radish. The lemon/lime wedges really brings out the flavour of the fish.

Monday, October 19, 2015

Beetroot risotto

One of my most popular dishes is beetroot risotto. It's both the recipe that I get asked for most often, and the recipe that people go out of their way to tell me that they enjoyed making. Here's the (quite simple!) recipe so that you can enjoy it too!

This recipe serves two, and is especially good with some slices of pan-roasted duck breast on top. Yum.


  • 1 beetroot
  • 1 large carrot
  • 1 small onion
  • Half a celery stick
  • 1 garlic clove
  • 1 tbsp olive oil
  • 100 g risotto rice
  • 30 g Parmesan cheese
  • Butter
  • Fresh parsley
  • Salt & pepper


First, peel the beetroot and carrot, and cut them into 1 cubes. Put them in a saucepan with a pinch of salt and enough water to cover them, bring them to the boil, and let them simmer for about 20 minutes.

While they're cooking, finely chop the onion, garlic and celery. Heat the olive oil in a large frying pan, and saute the chopped vegetables gently in the olive oil until they're soft and translucent. Also, grate the Parmesan, chop the parsley, and boil a full kettle.

Once the beetroot and carrot are cooked, strain off the liquid into a jug and set the vegetables to one side.

Turn up the heat in the frying pan, and gently fry the rice with the onion, garlic and celery for 1–2 minutes. Then add a little of the stock from cooking the beetroot and carrot (that you saved earlier in a jug), and stir the rice until almost all the liquid has been absorbed. Repeat until you run out of liquid. Add the root vegetables into the pan, and continue to gradually add hot water (from the kettle) while gently stirring until the rice is cooked.

Take the risotto of the heat, and stir in the Parmesan, the parsley, and a knob of butter. Let it rest for a minute, and serve in bowles with some freshly-ground black pepper on top!

Monday, October 12, 2015

Pan-roast venison haunch with pumpkin risotto

The rather awesome K. and I have been going out for three years! We made a special dinner to celebrate.

This recipe, unsurprisingly, serves two. Best accompanied by a nice Pinot Noir!


For the venison:

  • 12 oz (350 g) venison haunch, in one piece
  • 1 tbsp sunflower oil
  • 30 g butter
  • 25 ml gin
  • 1 tsp plain flour
  • 150 ml red wine
  • 300 ml lamb stock
  • 1 bay leaf
  • 1 small sprig rosemary
  • 5 juniper berries
  • Salt & pepper

For the risotto:

  • 1 tbsp olive oil
  • 1 onion
  • 2 cloves garlic
  • 1 celery stick
  • 300 g pumpkin
  • Some kale (a generous handful)
  • 100 g risotto rice
  • 150 ml white wine
  • 500 ml vegetable stock
  • 30 g Parmesan cheese
  • Butter
  • Salt & pepper

To serve:

  • Parsley leaves
  • Parmesan shavings

You will need a digital kitchen thermometer.


I'm listing the two methods separately, but you'll need to do them simultaneously. Make sure you have all the equipment and ingredients ready before you start!

For the venison:

  1. At least an hour in advance, remove the venison from the fridge, remove all packaging, and pat dry with a clean paper towel. Place it on a clean chopping board and leave to dry in the air.
  2. Put a roasting tin in the oven and preheat to 120 °C fan. Heat the sunflower oil in a heavy-based frying pan over a high heat.
  3. Season the venison with salt and pepper. Fry the venison for 1–2 minutes on each side until sealed and browned. Add the butter to the the pan and baste continuously for 3 minutes, turning occasionally, then transfer it to the preheated roasting tin in the oven.
  4. While the venison is in the oven, make sure to check it periodically with the thermometer — the aim is to reach 63 °C in the centre of the meat [1], but don't let it get any hotter than that, or it'll dry out! It'll need about 15–20 minutes in the oven.
  5. Deglaze the frying pan with the gin, then add the flour and mix to a paste. Add the red wine and herbs, and simmer over a high heat until reduced by half.
  6. Remove the rosemary (because otherwise it can overpower the other flavours), and add the lamb stock. Continue reducing until a sauce-like consistency is achieved. Sieve the gravy and set aside (but keep it warm!)
  7. Once the venison reaches the target temperature, remove it from the oven and cover it in foil to rest. Make sure to rest it for at least 5 minutes.

For the risotto:

  1. Finely chop the onion and celery, and crush the garlic. Dice the pumpkin into 1 cm cubes, and finely shred the kale. Grate the Parmesan.
  2. Heat the olive oil over a medium heat in a large, non-stick pan, and add the onion and celery. Saute the vegetables gently for about 5 minutes until they are soft but not browning.
  3. Add the garlic and rice, and continue to cook for 2–3 minutes.
  4. Turn the heat up to high, and add the white wine and some salt. Continue to cook, while stirring regularly and adding stock when the risotto starts to dry out.
  5. When the rice is starting to soften, add the pumpkin and kale. Continue to cook the risotto, adding liquid when needed, until the rice is soft but not mushy.
  6. Stir in the Parmesan and a generous knob of butter, and leave the risotto to rest for at least a minute.

To serve, carve the venison into thick, even rounds. Arrange the risotto and venison on pre-heated plates. Spoon a little of the gravy onto the venison, and top the risotto with freshly-ground black pepper, parsley leaves and Parmesan shavings.

[1] Getting the centre of the venison to 63 °C is recommended if you want to make sure that any bacteria or other nasties are fully killed off, and will result in having venison that's "done" — with a centre that's slightly pink but still deliciously tender. If you'd like medium-rare, aim for 57 °C.

Tuesday, September 29, 2015

Using C library functions from LiveCode Builder

This blog post is part of an ongoing series about writing LiveCode Builder applications without the LiveCode engine.

Currently, the LiveCode Builder (LCB) standard library is fairly minimal. This means that there are some types of task for which you'll want to go beyond the standard library.

In a previous post, I described how to use LiveCode's foundation library. This lets you access plenty of built-in LiveCode functionality that isn't directly exposed to LCB code yet.

Someone else's problem

Often someone's already wrapped the functions that you need in another program, especially on Linux. You can run that program as a subprocess to access it. In LiveCode Script, you could use the shell function to run an external program. Unfortunately, the LCB standard library doesn't have an equivalent feature yet!

On the other hand, the standard C library's system(3) function can be used to run a shell command. Its prototype is:

int system(const char *command);

In this post, I'll describe how LCB's foreign function interface lets you call it.

Declaring a foreign handler

As last time, you can use the foreign handler syntax to declare the C library function. The com.livecode.foreign provides some important C types.

use com.livecode.foreign

foreign handler _system(in pCommand as ZStringNative) \
      returns CInt binds to "system"

Some things to bear in mind here:

  • I've named the foreign handler _system because the all-lowercase identifier system is reserved for syntax tokens
  • The ZStringNative type automatically converts a LCB string into a null-terminated string into whatever encoding LiveCode thinks is the system's "native" encoding.
  • Because the C library is always linked into the LiveCode program when it's started, you don't need to specify a library name in the binds to clause; you can just use the name of the system(3) function.

Understanding the results

So, now you've declared the foreign handler, that's it! You can now just _system("rm -rf /opt/runrev") (or some other helpful operation). Right?

Well, not quite. If you want to know whether the shell command succeeded, you'll need to interpret the return value of the _system handler, and unfortunately, this isn't just the exit status of the command. From the system(3) man page:

The value returned is -1 on error (e.g., fork(2) failed), and the return status of the command otherwise. This latter return status is in the format specified in wait(2). Thus, the exit code of the command will be WEXITSTATUS(status). In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127).

So if the _system handler returns -1, then an error occurred. Otherwise, it's necessary to do something equivalent to the WIFEXITED C macro to check if the command ran normally. If it didn't, then some sort of abnormal condition occurred in the command (e.g. it was killed). Finally, the actual exit status is extracted by doing something equivalent to the WEXITSTATUS C macro.

On Linux, these two macros are defined as follows:

#define WIFEXITED(status)     __WIFEXITED (__WAIT_INT (status))
#define WEXITSTATUS(status)   __WEXITSTATUS (__WAIT_INT (status))
#define __WIFEXITED(status)   (__WTERMSIG(status) == 0)
#define __WEXITSTATUS(status) (((status) & 0xff00) >> 8)
#define __WTERMSIG(status)    ((status) & 0x7f)
#define __WAIT_INT(status)    (status)

Or, more succinctly:

#define WIFEXITED(status)   (((status) & 0x7f) == 0)
#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)

This is enough to be able to fully define a function that runs a shell command and returns its exit status.

module org.example.system

use com.livecode.foreign

private foreign handler _system(in pCommand as ZStringNative) \
      returns CInt binds to "system"

Run the shell command  and wait for it to finish.
Returns the exit status of if the command completed, and nothing
if an error occurred or the command exited abnormally.
public handler System(in pCommand as String) \
      returns optional Number

   variable tStatus as Number
   put _system(pCommand) into tStatus

   -- Check for error
   if tStatus is -1 then
      return nothing
   end if

   -- Check for abnormal exit
   if (127 bitwise and tStatus) is not 0 then
      return nothing
   end if

   -- Return exit status
   return 255 bitwise and (tStatus shifted right by 8 bitwise)

end module

Tip of the iceberg

This post has hopefully demonstrated the potential of LiveCode Builder's FFI. Even if you use only the C standard library's functions, you gain access to almost everything that the operating system is capable of!

Using a C function from LCB involves reading the manual pages to find out how the function should be used, and how best to map its arguments and return values onto LCB types; often, reading C library header files to understand how particular values should be encoded or decoded; and finally, binding the library function and providing a wrapper that makes it comfortable use from LCB programs.

LiveCode Builder can do a lot more than just making widgets and — as I hope I've demonstrated — can do useful things without the rest of the LiveCode engine. Download LiveCode 8 and try some things out!

Wednesday, September 23, 2015

Roasted vegetable and chickpea tagine

It's been a while since I last posted a recipe here! Recently I've been having quite a lot of success with this Morrocan-inspired vegetarian recipe.

This recipe makes 6 portions.


For the roasted vegetables:

  • 350 g new potatoes, halved
  • 1 fennel bulb, trimmed & cut into batons
  • 1 medium carrot, cut into chunks
  • 1 large red pepper, cut into chunks
  • 1 large red onion, cut into chunks
  • 3 tbsp exra-virgin olive oil
  • 1 tsp cumin seeds
  • 1 tsp fennel seeds
  • 1 tsp coriander seeds, crushed

For the sauce:

  • 4 garlic cloves, chopped
  • 400 g canned chopped tomatoes
  • 400 g canned chickpeas, drained and rinsed
  • 250 ml red wine
  • 1 pickled lemon, finely chopped
  • 0.5 tbsp harissa paste
  • 1 tsp ras el hanout
  • 1 cinnamon stick
  • 40 g whole almonds
  • 10 dried apricots, halved

To serve:

  • Greek-style yoghurt
  • 2 tbsp coriander, finely chopped


Preheat the oven to 200 °C fan. Put all the ingredients for the roasted vegetables into a large, heavy roasting tin, season to taste, and toss together to coat the vegetables in oil and spices. Roast for 30 minutes until the potatoes are cooked through and the vegetables generally have a nice roasted tinge.

While the vegetables are roasting, heat a large pan over a medium heat. Fry the garlic for 20–30 seconds until fragrant. Add the remaining ingredients, bring to the boil, and simmer while the vegetables roast.

When the vegetables are roasted, add them to the sauce and stir. Return the sauce to the simmer for another 15–20 minutes.

Serve in bowls, topped with a dollop of yoghurt and some chopped coriander. Couscous makes a good accompaniment to this dish if you want to make it go further.

Monday, September 14, 2015

Compiling multi-module LiveCode Builder programs

This blog post is part of an ongoing series about writing LiveCode Builder applications without the LiveCode engine.

Multi-module programs

When writing a large program, it's often useful to break it down into more than one module. For example, you might want to make a module that's dedicated to loading and saving the program's data, which has quite a lot of internal complexity but exposes a very simple API with Load() and Save() handlers. This is handy for making sure that it's easy to find the source file where each piece of functionality is located.

However, it can become tricky to compile the program. Each module may depend on any number of other modules, and you have to compile them in the correct order or the compilation result may be incorrect. Also, if one module changes, you have to recompile all of the modules that depend on it. If you tried to do this all by hand, it would be nigh-on impossible to correctly compile your program once you got above about 10 source files.

Fortunately, there are two really useful tools that can make it all rather easy. GNU Make (the make command) can perform all the required build steps in the correct order (and even in parallel!). And to help you avoid writing Makefiles by hand, lc-compile has a useful --deps mode.

Most of the remainder of this blog post will assume some familiarity with make and common Unix command-line tools.

The --deps option for lc-compile

make lets you express dependencies between files. However, you already express the dependencies between LCB source files when you write a use declaration. For example:

use com.livecode.foreign

says that your module depends on the .lci (LiveCode Interface) file for the com.livecode.foreign module.

So, the LCB compiler (a) already knows all the dependencies between the source files of your project and (b) already knows how to find the files. To take advantage of this and to massively simplify the process of creating a Makefile for a LCB project, lc-compile provides a --deps mode. In --deps mode, lc-compile doesn't do any of the normal compilation steps; instead, it outputs a set of Make rules on standard output.

Consider the following trivial two-file program.

-- org.example.numargs.lcb

module org.example.numargs

public handler NumArgs()
   return the number of elements in the command arguments
end handler

end module
-- org.example.countargs.lcb

module org.example.countargs

use org.example.numargs

public handler Main()
   quit with status NumArgs()
end handler

end module

To generate the dependency rules, you run lc-compile with almost a normal command line — but you specify --deps make instead of an --output argument, and you list all of your source files instead of just one of them. See also my previous blog post about compiling and running pure LCB programs. For the "countargs" example program you could run:

$TOOLCHAIN/lc-compile --modulepath . --modulepath $TOOLCHAIN/modules/lci --deps make org.example.numargs.lcb org.example.countargs.lcb

This would print the following rules:

org.example.countargs.lci: org.example.numargs.lci org.example.countargs.lcb
org.example.numargs.lci: org.example.numargs.lcb

Integrating with make

You can integrate this info into a Makefile quite easily. There are two pieces that you need: 1) tell make to load the extra rules, and 2) tell make how to generate them. In particular, it's important to regenerate the rules whenever the Makefile itself is modified (e.g. to add an additional source file).

# List of source code files
SOURCES = org.example.countargs.lcb org.example.numargs.lcb

# Include all the generated dependency rules

# Rules for regenerating dependency rules whenever
# the source code changes $(SOURCES) Makefile
 $(TOOLCHAIN)/lc-compile --modulepath . --modulepath $(TOOLCHAIN)/modules/lci --deps make -- $(SOURCES) > $@

A complete Makefile

Putting this all together, I've created a complete Makefile for the example multi-file project. It has the usual make compile and make clean targets, and places all of the built artefacts in a subdirectory called _build.

# Parameters

# Tools etc.
LC_SRC_DIR ?= ../livecode
LC_BUILD_DIR ?= $(LC_SRC_DIR)/build-linux-x86_64/livecode/out/Debug
LC_LCI_DIR = $(LC_BUILD_DIR)/modules/lci
LC_COMPILE ?= $(LC_BUILD_DIR)/lc-compile
LC_RUN ?= $(LC_BUILD_DIR)/lc-run

BUILDDIR = _build

LC_COMPILE_FLAGS += --modulepath $(BUILDDIR) --modulepath $(LC_LCI_DIR)

# List of source code files.
SOURCES = org.example.countargs.lcb org.example.numargs.lcb

# List of compiled module filenames.
MODULES = $(patsubst %.lcb,$(BUILDDIR)/%.lcm,$(SOURCES))

# Top-level targets
all: compile

compile: $(MODULES)

 -rm -rf $(BUILDDIR)

.PHONY: all compile clean

# Build dependencies rules
include $(BUILDDIR)/

 mkdir -p $(BUILDDIR)

 $(LC_COMPILE) $(LC_COMPILE_FLAGS) --deps make -- $(SOURCES) > $@

# Build rules
$(BUILDDIR)/%.lcm $(BUILDDIR)/%.lci: %.lcb | $(BUILDDIR)
 $(LC_COMPILE) $(LC_COMPILE_FLAGS) --output $@ -- $<

You should be able to use this directly in your own projects. All you need to do is to modify the list of source files in the SOURCES variable!

Note that you need to name your source files exactly the same as the corresponding interface files in order for this Makefile to work correctly. I'll leave adapting to the case where the source file and interface file are named differently as an exercise to the reader…

I hope you find this useful as a basis for writing new LiveCode Builder projects! Let me know how you get on.

Sunday, September 06, 2015

Accessing the Foundation library with LiveCode Builder

This blog post is part of an ongoing series about writing LiveCode Builder applications without the LiveCode engine.

The LiveCode Foundation library

LiveCode includes a "foundation" library (called, unsurprisingly, libfoundation) which provides a lot of useful functions that work on all the platforms that LiveCode supports. This is used to make sure that LiveCode works in the same way no matter which operating system or processor you're using. libfoundation is compiled into both the LiveCode engine and LiveCode Builder's lc-run tool, so it's always available.

libfoundation is written in C and C++. The functions available in the library are declared in the foundation.h header file.

Among other capabilities, libfoundation handles encoding and decoding text. This provides an opportunity to fix one of the problems with the "hello world" program I described in a previous post.

Foreign function access to libfoundation

The "hello world" program read in a file and wrote it out to the standard output stream. Unlike "hello world" programs seen elsewhere, it *didn't* write out a string, e.g.:

write "Hello World!" to the output stream

This doesn't work because write needs to receive Data, and converting a String to Data requires encoding (using a suitable string encoding). And unfortunately, the LiveCode Builder library doesn't supply any text encoding/decoding syntax, although I'm working on it.

However, and fortunately for this blog post, libfoundation supplies a suitable function, MCStringEncode. Its C++ declaration looks like:

bool MCStringEncode(MCStringRef string, MCStringEncoding encoding, bool is_external_rep, MCDataRef& r_data);

You can use it in a LiveCode Builder program by declaring it as a foreign handler. The com.livecode.foreign module provides some helpful declarations for C and C++ types.

use com.livecode.foreign

foreign handler MCStringEncode(in Source as String, \
      in Encoding as CInt, in IsExternalRep as CBool, \
      out Encoded as Data) returns CBool binds to "<builtin>"

CInt and CBool are C & C++'s int and bool types, respectively.

Encoding a string with UTF-8

Next, you can write a LiveCode Builder handler that encodes a string using UTF-8 (an 8-bit Unicode encoding). Almost every operating system will Do The Right Thing if you write UTF-8 encoded text to standard output; the only ones that might complain are some versions of Windows and some weirdly-configured Linux systems.

handler EncodeUTF8(in pString as String) returns Data
   variable tEncoded as Data
   MCStringEncode(pString, 4 /* UTF-8 */, false, tEncoded)
   return tEncoded
end handler

The "4" in there is a magic number that comes from libfoundation's kMCStringEncodingUTF8 constant. Also, you should always pass false to the IsExternalRep argument (for historical reasons).

A better "hello world" program

Putting this all together, you can now write an improved "hello world" program that doesn't get its text from an external file.

module org.example.helloworld2

use com.livecode.foreign

foreign handler MCStringEncode(in Source as String, \
      in Encoding as CInt, in IsExternalRep as CBool, \
      out Encoded as Data) returns CBool binds to "<builtin>"

handler EncodeUTF8(in pString as String) returns Data
   variable tEncoded as Data
   MCStringEncode(pString, 4 /* UTF-8 */, false, tEncoded)
   return tEncoded
end handler

public handler Main()
   write EncodeUTF8("Hello World!\n") to the output stream
end handler

end module

If you compile and run this program, you'll now get the same "Hello World!" message -- but this time, it's taking some text, turning it into Data by encoding it, and writing it out, rather than just regurgitating some previously-encoded data.

Other neat stuff

There's other cool (and, often, terribly unsafe) stuff you can do with direct access to libfoundation functions, like allocate Pointers to new memory buffers and directly manipulate LiveCode types & values. However, most of libfoundation's capabilities are already available using normal LiveCode Builder syntax.

The real power of foreign handler declarations becomes apparent when accessing functions that aren't in libfoundation — and this may be the subject of a future blog post!

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.