Lua Server Pages

Lua Server Pages, a Lua scripting engine plug-in that blows away any other approach to embedded web development, offers a dramatic short cut to traditional C server pages. The Lua Server Pages plug-in (included in the Barracuda Application Server) makes developing applications extremely fast, simple, and safe. Notably, it isn't limited to developing web applications, but can also be used to provide high-level socket APIs that make developing secure socket applications very easy. We also provide HTTP client libraries, SMTP (mail) libraries, etc. In fact, we estimate that up to 80% of a modern embedded system could be implemented using Lua Server Pages, ensuring dramatically reduced development time.

How Lua Works

Lua is a scripting language and includes a compiler that compiles the scripts on the fly to a register-based instruction set. The instruction set is then executed by the virtual machine. The Lua compiler and the virtual machine are implemented in C code and can run in virtually any device with sufficient memory. The Barracuda Application Server, the Lua Server Pages plug-in, and all plug-ins required by the Lua Server Pages use roughly 600 kbytes of ROM. We recommend a minimum of 500 kbytes RAM for running your scripts.

Lua is the only module in the Barracuda Application Server not developed by Real Time Logic. Lua is licensed under the terms of the MIT license and can therefore be used in any commercial product without any restrictions. See the Lua license for more information.

Customers Discover the Benefits of Lua Server Pages

We have customers using the Lua Server Pages plug-in in everything from deeply embedded monolithic systems, such as in Xilinx FPGAs, to high-level host systems, including Linux and Windows. The Lua virtual machine can be compiled for any microprocessor architecture, including CPUs embedded inside FPGAs such as an 80MHz ARM processor that is sufficient to run the Lua virtual machine. The web page you are reading now is managed by Lua Server Pages.

Customer Testimonial:

"It was great that you drove the point across regarding the benefits of using Lua. I think that most embedded people, such as myself, would initially gravitate towards C code and CSP because it's a familiar environment and disregard Lua without realizing the huge benefits that can be gained with only a little bit of an upfront cost of having to learn the Lua language. BTW, you definitely made us think about Lua a whole lot more and not just as a server-side scripting language but as a scripting language that we could potentially use throughout our entire project." - Honeywell employee attending our three day training.

Lua Server Pages Advantages for Embedded Web Development

Lua is not your average scripting language. Lua is small, fast, portable, and embeddable. These cornerstones of the language make it well-suited for embedded devices, web applications, and protocol stacks.

  • Lua Server Pages are designed to be embedded.
  • Lua Server Pages use one of the fastest scripting languages available.
  • Lua Server Pages are small.
  • Lua Server Pages are easy to learn.
  • Lua Server Pages are designed to help you cut development cost and time to market.

See our whitepaper Lua fast-tracks embedded web application development for more information on how Lua simplifies and speeds up the development process.

Lua Server Pages Development Process

The Lua Server Pages development cycle is a straight-forward, three-step process. Recompiling a modified Lua Server Page and executing the Lua Server Page is as simple as refreshing the browser window. When the browser sends the request, the server loads, compiles, and executes the new script. The figure to the right illustrates this process.

The Lua Server Pages plug-in comprises the Lua engine and interface logic to the Barracuda Application Server's C API. The interface logic is referred to as a "Lua binding" and makes it possible for Lua scripts to call C functions. We provide bindings to most of the C APIs in the Barracuda Application Server, including plug-ins such as the WebDAV Server. The Lua Server Pages plug-in is designed to be extended and lets you add your own device management APIs. We also provide an online C parser that automatically generates Lua bindings for your C device management APIs.

Lua Server Pages require that you provide some start-up code in C, but no C code is required when assembling web-server components. All components, including opening server listen ports, managing SSL certificates and so on, can be dynamically managed from within a Lua script. Lua also makes it possible to dynamically add and remove server components in a running system. For example, you can dynamically change the listen port numbers without restarting the server.

Refresh Browser

Lua Server Pages Development Process

A question that is frequently asked by our customers is how to access their own proprietary C functions from Lua code. Lua to C/C++ access is called a Lua binding and can be created manually or by using our automatic Lua Binding Generator. See the online tutorials and how to interface Lua to existing C/C++ code for more information.

Learn Lua in 15 Minutes

You can be a Lua expert in 15 minutes!

-- Two dashes start a one-line comment.

     Adding two ['s and ]'s makes it a
     multi-line comment.

-- 1. Variables and flow control.

num = 42  -- Numbers are doubles or integers.
-- Numbers can be set to integers only when compiling the Lua C code engine.

s = 'walternate'  -- Immutable strings like Python.
t = "double-quotes are also fine"
u = [[ Double brackets
       start and end
       multi-line strings.]]
t = nil  -- Undefines t; Lua has garbage collection.

-- Blocks are denoted with keywords like do/end:
while num < 50 do
  num = num + 1  -- No ++ or += type operators.

-- If clauses:
if num > 40 then
  print('over 40')
elseif s ~= 'walternate' then  -- ~= is not equals.
  -- Equality check is == like Python; ok for strs.
  io.write('not over 40\n')  -- Defaults to stdout.
  -- Variables are global by default.
  thisIsGlobal = 5  -- Camel case is common.

  -- How to make a variable local:
  local line =  -- Reads next stdin line.

  -- String concatenation uses the .. operator:
  print('Winter is coming, ' .. line)

-- Undefined variables return nil.
-- This is not an error:
foo = anUnknownVariable  -- Now foo = nil.

aBoolValue = false

-- Only nil and false are falsy; 0 and '' are true!
if not aBoolValue then print('twas false') end

-- 'or' and 'and' are short-circuited.
-- This is similar to the a?b:c operator in C/js:
ans = aBoolValue and 'yes' or 'no'  --> 'no'

karlSum = 0
for i = 1, 100 do  -- The range includes both ends.
  karlSum = karlSum + i

-- Use "100, 1, -1" as the range to count down:
fredSum = 0
for j = 100, 1, -1 do fredSum = fredSum + j end

-- In general, the range is begin, end[, step].

-- Another loop construct:
  print('the way of the future')
  num = num - 1
until num == 0

-- 2. Functions.

function fib(n)
  if n < 2 then return 1 end
  return fib(n - 2) + fib(n - 1)

-- Closures and anonymous functions are ok:
function adder(x)
  -- The returned function is created when adder is
  -- called, and remembers the value of x:
  return function (y) return x + y end
a1 = adder(9)
a2 = adder(36)
print(a1(16))  --> 25
print(a2(64))  --> 100

-- Returns, func calls, and assignments all work
-- with lists that may be mismatched in length.
-- Unmatched receivers are nil;
-- unmatched senders are discarded.

x, y, z = 1, 2, 3, 4
-- Now x = 1, y = 2, z = 3, and 4 is thrown away.

function bar(a, b, c)
  print(a, b, c)
  return 4, 8, 15, 16, 23, 42

x, y = bar('zaphod')  --> prints "zaphod  nil nil"
-- Now x = 4, y = 8, values 15..42 are discarded.

-- Functions are first-class, may be local/global.
-- These are the same:
function f(x) return x * x end
f = function (x) return x * x end

-- And so are these:
local function g(x) return math.sin(x) end
local g; g  = function (x) return math.sin(x) end
-- the 'local g' decl makes g-self-references ok.

-- Trig funcs work in radians, by the way.

-- Calls with one string param don't need parens:
print 'hello'  -- Works fine.

-- 3. Tables.

-- Tables = Lua's only compound data structure;
--          they are associative arrays.
-- Similar to php arrays or js objects, they are
-- hash-lookup dicts that can also be used as lists.

-- Using tables as dictionaries / maps:

-- Dict literals have string keys by default:
t = {key1 = 'value1', key2 = false}

-- String keys can use js-like dot notation:
print(t.key1)  -- Prints 'value1'.
t.newKey = {}  -- Adds a new key/value pair.
t.key2 = nil   -- Removes key2 from the table.

-- Literal notation for any (non-nil) value as key:
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28])  -- prints "tau"

-- Key matching is basically by value for numbers
-- and strings, but by identity for tables.
a = u['@!#']  -- Now a = 'qbert'.
b = u[{}]     -- We might expect 1729, but it's nil:
-- b = nil since the lookup fails. It fails
-- because the key we used is not the same object
-- as the one used to store the original value. So
-- strings & numbers are more portable keys.

-- A one-table-param function call needs no parens:
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'}  -- Prints 'Sonmi~451'.

for key, val in pairs(u) do  -- Table iteration.
  print(key, val)

-- _G is a special table of all globals.
print(_G['_G'] == _G)  -- Prints 'true'.

-- Using tables as lists / arrays:

-- List literals implicitly set up int keys:
v = {'value1', 'value2', 1.21, 'gigawatts'}
for i = 1, #v do  -- #v is the size of v for lists.
  print(v[i])  -- Indices start at 1 !! SO CRAZY!
-- A 'list' is not a real type. v is just a table
-- with consecutive integer keys, treated as a list.

-- 3.1 Metatables and metamethods.

-- A table can have a metatable that gives the table
-- operator-overloadish behavior. Later we'll see
-- how metatables support js-prototypey behavior.

f1 = {a = 1, b = 2}  -- Represents the fraction a/b.
f2 = {a = 2, b = 3}

-- This would fail:
-- s = f1 + f2

metafraction = {}
function metafraction.__add(f1, f2)
  sum = {}
  sum.b = f1.b * f2.b
  sum.a = f1.a * f2.b + f2.a * f1.b
  return sum

setmetatable(f1, metafraction)
setmetatable(f2, metafraction)

s = f1 + f2  -- call __add(f1, f2) on f1's metatable

-- f1, f2 have no key for their metatable, unlike
-- prototypes in js, so you must retrieve it as in
-- getmetatable(f1). The metatable is a normal table
-- with keys that Lua knows about, like __add.

-- But the next line fails since s has no metatable:
-- t = s + s
-- Class-like patterns given below would fix this.

-- An __index on a metatable overloads dot lookups:
defaultFavs = {animal = 'gru', food = 'donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal  -- works! thanks, metatable

-- Direct table lookups that fail will retry using
-- the metatable's __index value, and this recurses.

-- An __index value can also be a function(tbl, key)
-- for more customized lookups.

-- Values of __index,add, .. are called metamethods.
-- Full list. Here a is a table with the metamethod.

-- __add(a, b)                     for a + b
-- __sub(a, b)                     for a - b
-- __mul(a, b)                     for a * b
-- __div(a, b)                     for a / b
-- __mod(a, b)                     for a % b
-- __pow(a, b)                     for a ^ b
-- __unm(a)                        for -a
-- __concat(a, b)                  for a .. b
-- __len(a)                        for #a
-- __eq(a, b)                      for a == b
-- __lt(a, b)                      for a < b
-- __le(a, b)                      for a <= b
-- __index(a, b)  <fn or a table>  for a.b
-- __newindex(a, b, c)             for a.b = c
-- __call(a, ...)                  for a(...)

-- 3.2 Class-like tables and inheritance.

-- Classes aren't built in; there are different ways
-- to make them using tables and metatables.

-- Explanation for this example is below it.

Dog = {}                                   -- 1.

function Dog:new()                         -- 2.
  newObj = {sound = 'woof'}                -- 3.
  self.__index = self                      -- 4.
  return setmetatable(newObj, self)        -- 5.

function Dog:makeSound()                   -- 6.
  print('I say ' .. self.sound)

mrDog = Dog:new()                          -- 7.
mrDog:makeSound()  -- 'I say woof'         -- 8.

-- 1. Dog acts like a class; it's really a table.
-- 2. function tablename:fn(...) is the same as
--    function tablename.fn(self, ...)
--    The : just adds a first arg called self.
--    Read 7 & 8 below for how self gets its value.
-- 3. newObj will be an instance of class Dog.
-- 4. self = the class being instantiated. Often
--    self = Dog, but inheritance can change it.
--    newObj gets self's functions when we set both
--    newObj's metatable and self's __index to self.
-- 5. Reminder: setmetatable returns its first arg.
-- 6. The : works as in 2, but this time we expect
--    self to be an instance instead of a class.
-- 7. Same as, so self = Dog in new().
-- 8. Same as mrDog.makeSound(mrDog); self = mrDog.


-- Inheritance example:

LoudDog = Dog:new()                           -- 1.

function LoudDog:makeSound()
  s = self.sound .. ' '                       -- 2.
  print(s .. s .. s)

seymour = LoudDog:new()                       -- 3.
seymour:makeSound()  -- 'woof woof woof'      -- 4.

-- 1. LoudDog gets Dog's methods and variables.
-- 2. self has a 'sound' key from new(), see 3.
-- 3. Same as, and converted to
-- as LoudDog has no 'new' key,
--    but does have __index = Dog on its metatable.
--    Result: seymour's metatable is LoudDog, and
--    LoudDog.__index = LoudDog. So seymour.key will
--    = seymour.key, LoudDog.key, Dog.key, whichever
--    table is the first with the given key.
-- 4. The 'makeSound' key is found in LoudDog; this
--    is the same as LoudDog.makeSound(seymour).

-- If needed, a subclass's new() is like the base's:
function LoudDog:new()
  newObj = {}
  -- set up newObj
  self.__index = self
  return setmetatable(newObj, self)

-- 4. Modules.

--[[ I'm commenting out this section so the rest of
--   this script remains runnable.

-- Suppose the file mod.lua looks like this:
local M = {}

local function sayMyName()

function M.sayHello()
  print('Why hello there')

return M

-- Another file can use mod.lua's functionality:
local mod = require('mod')  -- Run the file mod.lua.

-- require is the standard way to include modules.
-- require acts like:     (if not cached; see below)
local mod = (function ()
  <contents of mod.lua>
-- It's like mod.lua is a function body, so that
-- locals inside mod.lua are invisible outside it.

-- This works because mod here = M in mod.lua:
mod.sayHello()  -- Says hello to Hrunkner.

-- This is wrong; sayMyName only exists in mod.lua:
mod.sayMyName()  -- error

-- require's return values are cached so a file is
-- run at most once, even when require'd many times.

-- Suppose mod2.lua contains "print('Hi!')".
local a = require('mod2')  -- Prints Hi!
local b = require('mod2')  -- Doesn't print; a=b.

-- dofile is like require without caching:
dofile('mod2.lua')  --> Hi!
dofile('mod2.lua')  --> Hi! (runs it again)

-- loadfile loads a lua file but doesn't run it yet.
f = loadfile('mod2.lua')  -- Call f() to run it.

-- loadstring is loadfile for strings.
g = loadstring('print(343)')  -- Returns a function.
g()  -- Prints out 343; nothing printed before now.


Lua code contributed by