Home Courses Lua Day 5
Day 05 Day 5

Day 5

Day 5

~1 hour Intermediate Hands-on Precision AI Academy

Today's Objective

The Lua C API, lua_State, calling Lua from C, calling C from Lua, coroutines for cooperative multitasking, and sandboxing untrusted scripts.

The Lua C API

Lua is designed to be embedded. The entire runtime is a single C library (liblua). You interact with it through a stack-based API: push values onto the Lua stack, call functions, and pop results. All communication between C and Lua flows through this stack.

Setup: Install lua and headers with brew install lua (macOS) or apt install liblua5.4-dev (Ubuntu). Compile with gcc main.c -llua -lm -o app.

Hello World — C Runs a Lua Script

c_-_run_a_lua_script.txt
C — RUN A LUA SCRIPT
#include <stdio.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

int main(void) {
    // 1. Create a Lua state (the interpreter)
    lua_State *L = luaL_newstate();
    if (!L) { fprintf(stderr, "out of memory\n"); return 1; }

    // 2. Open standard libraries (print, math, string, etc.)
    luaL_openlibs(L);

    // 3. Execute a Lua file
    int err = luaL_dofile(L, "config.lua");
    if (err) {
        // The error message is on top of the stack
        fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));
        lua_pop(L, 1);
    }

    // 4. Execute a string of Lua code
    luaL_dostring(L, "print('Hello from C!')");

    // 5. Read a global variable from Lua
    lua_getglobal(L, "my_value");          // push _G["my_value"]
    if (lua_isnumber(L, -1)) {
        double v = lua_tonumber(L, -1);
        printf("my_value = %g\n", v);
    }
    lua_pop(L, 1);                          // pop the value

    // 6. Cleanup
    lua_close(L);
    return 0;
}

Calling Lua Functions from C

c_-_call_lua_function.txt
C — CALL LUA FUNCTION
// Lua script loaded earlier defines:
//   function add(a, b) return a + b end

// Call add(10, 32) from C and get the result
lua_getglobal(L, "add");       // push function
lua_pushnumber(L, 10);         // push arg 1
lua_pushnumber(L, 32);         // push arg 2

// lua_pcall(L, nargs, nresults, error_handler_index)
// Returns LUA_OK (0) on success
if (lua_pcall(L, 2, 1, 0) != LUA_OK) {
    fprintf(stderr, "call error: %s\n", lua_tostring(L, -1));
    lua_pop(L, 1);
} else {
    double result = lua_tonumber(L, -1);
    printf("add(10, 32) = %g\n", result);   // 42.0
    lua_pop(L, 1);
}

// Always use lua_pcall (protected call) rather than lua_call
// lua_pcall catches Lua errors; lua_call panics on error

Registering C Functions in Lua

c_-_expose_c_functions_to_lua.txt
C — EXPOSE C FUNCTIONS TO LUA
// A C function callable from Lua must have this signature:
//   typedef int (*lua_CFunction)(lua_State *L);
// Arguments come from the stack; return values go onto the stack.
// Return value = number of results pushed.

static int c_sqrt(lua_State *L) {
    // luaL_checknumber: get arg 1 as a number (errors if wrong type)
    double n = luaL_checknumber(L, 1);
    lua_pushnumber(L, sqrt(n));
    return 1;  // one result
}

static int c_greet(lua_State *L) {
    const char *name = luaL_checkstring(L, 1);
    lua_pushfstring(L, "Hello, %s!", name);
    return 1;
}

// Register a library of C functions
static const luaL_Reg mylib[] = {
    {"sqrt",  c_sqrt},
    {"greet", c_greet},
    {NULL, NULL}   // sentinel
};

// Call this once after luaL_newstate()
static void register_mylib(lua_State *L) {
    luaL_newlib(L, mylib);           // create table with functions
    lua_setglobal(L, "mylib");       // _G["mylib"] = table
}

// Now in Lua:
//   print(mylib.sqrt(16))      --> 4.0
//   print(mylib.greet("world")) --> Hello, world!

Coroutines

Lua coroutines are cooperative threads — they run on a single OS thread, pausing with coroutine.yield() and resuming with coroutine.resume(). They're perfect for game AI, async-style I/O, and iterators.

lua_-_coroutines.txt
LUA — COROUTINES
-- Producer / consumer with coroutines
local function producer()
  local items = {"apple", "banana", "cherry", "date"}
  for _, item in ipairs(items) do
    coroutine.yield(item)    -- pause and send value to consumer
  end
  return "done"              -- final return value
end

local co = coroutine.create(producer)

while true do
  local ok, value = coroutine.resume(co)
  if not ok then
    print("error:", value)
    break
  end
  if coroutine.status(co) == "dead" then
    print("producer finished:", value)
    break
  end
  print("received:", value)
end
-- received: apple
-- received: banana
-- received: cherry
-- received: date
-- producer finished: done

-- Coroutine as a generator (wrap makes it iterator-like)
local function range(from, to, step)
  step = step or 1
  return coroutine.wrap(function()
    for i = from, to, step do
      coroutine.yield(i)
    end
  end)
end

for n in range(1, 10, 2) do
  io.write(n .. " ")
end
-- 1 3 5 7 9

Sandboxing Untrusted Lua

lua_-_sandbox.txt
LUA — SANDBOX
-- Build a restricted environment for untrusted code
local function make_sandbox()
  return {
    -- Safe standard library subset
    print   = print,
    pairs   = pairs,
    ipairs  = ipairs,
    next    = next,
    type    = type,
    tostring = tostring,
    tonumber = tonumber,
    math    = {
      abs=math.abs, floor=math.floor, ceil=math.ceil,
      min=math.min, max=math.max, sqrt=math.sqrt,
      random=math.random, pi=math.pi,
    },
    string  = {
      format=string.format, len=string.len,
      sub=string.sub, upper=string.upper, lower=string.lower,
      gmatch=string.gmatch, gsub=string.gsub,
    },
    table   = {
      insert=table.insert, remove=table.remove,
      sort=table.sort, concat=table.concat,
    },
    -- NO: io, os, require, load, dofile, loadfile, package
  }
end

local function run_sandboxed(code, sandbox)
  local fn, err = load(code, "sandbox", "t", sandbox)
  if not fn then return false, err end
  local ok, result = pcall(fn)
  return ok, result
end

local sandbox = make_sandbox()
local code = [[
  local sum = 0
  for i = 1, 10 do sum = sum + i end
  return sum
]]

local ok, result = run_sandboxed(code, sandbox)
print(ok, result)   --> true    55

-- Attempt to break out
local bad = "return io.open('/etc/passwd')"
local ok2, err2 = run_sandboxed(bad, sandbox)
print(ok2, err2)    --> false   [string "sandbox"]:1: attempt to index a nil value (global 'io')
Add a timeout. A sandboxed script can still hang with an infinite loop. Use lua_sethook in C with a count hook to inject a check every N instructions, and abort if a time limit is exceeded.
Exercise
Build a C Application with a Lua Config and Plugin System
  1. Write a C program that reads a config.lua file at startup. The config defines server settings (host, port, max_connections) as a Lua table. Parse them in C with the stack API.
  2. Add a plugins/ directory. At startup, scan it with opendir/readdir and luaL_dofile each .lua file. Each plugin is expected to expose an on_request(path) function.
  3. Implement a minimal HTTP-like dispatch loop in C: read a path string from stdin, find the matching plugin's on_request function, call it via lua_pcall, and print the return value.
  4. Run each plugin in its own sandboxed environment so plugins can't access each other or the C host's globals directly.
  5. Add a coroutine.wrap-based generator in Lua that the C host can call repeatedly to stream a large response in chunks.

You finished Lua in 5 Days.

You went from syntax to tables, metatables, a playable game, and a C embedding. Lua is now in your toolkit — the scripting layer that makes every C/C++ application extensible.

Take the Live Bootcamp →

Course Complete

Completing all five days means having a solid working knowledge of Lua. The skills here translate directly to real projects. The next step is practice — pick a project and build something with what was learned.

Supporting Videos & Reading

Go deeper with these external references.

Day 5 Checkpoint

Before moving on, verify you can answer these without looking:

Live Bootcamp

Learn this in person — 2 days, 5 cities

Thu–Fri sessions in Denver, Los Angeles, New York, Chicago, and Dallas. $1,490 per seat. June–October 2026.

Reserve Your Seat →
Back to Course
Lua — Full Course Overview