mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
examples/lua: Add REPL.
This example add a module thats starts an interactive READ-EVAL- PRINT-LOOP written in Lua.
This commit is contained in:
parent
ed4411602c
commit
87496f3376
78
examples/lua_REPL/Makefile
Normal file
78
examples/lua_REPL/Makefile
Normal file
@ -0,0 +1,78 @@
|
||||
APPLICATION = lua_repl
|
||||
|
||||
# If no BOARD is found in the environment, use this default:
|
||||
BOARD ?= native
|
||||
|
||||
# This has to be the absolute path to the RIOT base directory:
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
BOARD_INSUFFICIENT_MEMORY := bluepill calliope-mini cc2650-launchpad \
|
||||
cc2650stk maple-mini microbit nrf51dongle \
|
||||
nucleo-f030r8 nucleo-f031k6 nucleo-f042k6 \
|
||||
nucleo-f070rb nucleo-f072rb nucleo-f103rb \
|
||||
nucleo-f302r8 nucleo-f303k8 nucleo-f334r8 \
|
||||
nucleo-f410rb nucleo-l031k6 nucleo-l053r8 \
|
||||
opencm904 spark-core stm32f0discovery \
|
||||
stm32mindev airfy-beacon arduino-mkr1000 \
|
||||
arduino-mkrfox1200 arduino-mkrzero arduino-zero \
|
||||
b-l072z-lrwan1 cc2538dk ek-lm4f120xl feather-m0 \
|
||||
ikea-tradfri limifrog-v1 mbed_lpc1768 nrf6310 \
|
||||
nucleo-f091rc nucleo-l073rz nz32-sc151 \
|
||||
openmote-cc2538 pba-d-01-kw2x remote-pa \
|
||||
remote-reva remote-revb samd21-xpro saml21-xpro \
|
||||
samr21-xpro seeeduino_arch-pro slstk3401a \
|
||||
sltb001a slwstk6220a sodaq-autonomo \
|
||||
sodaq-explorer stk3600 stm32f3discovery \
|
||||
yunjia-nrf51822
|
||||
|
||||
BOARD_BLACKLIST := arduino-duemilanove arduino-mega2560 arduino-uno \
|
||||
chronos hifive1 jiminy-mega256rfr2 mega-xplained mips-malta \
|
||||
msb-430 msb-430h pic32-clicker pic32-wifire telosb \
|
||||
waspmote-pro wsn430-v1_3b wsn430-v1_4 z1
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
DEVELHELP ?= 1
|
||||
|
||||
# Uncomment the following lines to enable debugging features.
|
||||
#CFLAGS_OPT = -Og
|
||||
#CFLAGS += -DDEBUG_ASSERT_VERBOSE -DLUA_DEBUG
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
# This value is in excess because we are not sure of the exact requirements of
|
||||
# lua (see the package's docs). It must be fixed in the future by taking
|
||||
# appropriate measurements.
|
||||
CFLAGS += -DTHREAD_STACKSIZE_MAIN='(THREAD_STACKSIZE_DEFAULT+7000)'
|
||||
|
||||
USEPKG += lua
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
# The code below generates a header file from any .lua scripts in the
|
||||
# example directory. The header file contains a byte array of the
|
||||
# ASCII characters in the .lua script.
|
||||
|
||||
LUA_PATH := $(BINDIR)/lua
|
||||
|
||||
# add directory of generated *.lua.h files to include path
|
||||
CFLAGS += -I$(LUA_PATH)
|
||||
|
||||
# generate .lua.h header files of .lua files
|
||||
LUA = $(wildcard *.lua)
|
||||
|
||||
LUA_H := $(LUA:%.lua=$(LUA_PATH)/%.lua.h)
|
||||
|
||||
$(LUA_PATH)/:
|
||||
@mkdir -p $@
|
||||
|
||||
# FIXME: This way of embedding lua code is not robust. A proper script will
|
||||
# be included later.
|
||||
|
||||
$(LUA_H): | $(LUA_PATH)/
|
||||
$(LUA_H): $(LUA_PATH)/%.lua.h: %.lua
|
||||
xxd -i $< | sed 's/^unsigned/const unsigned/g' > $@
|
||||
|
||||
$(RIOTBUILD_CONFIG_HEADER_C): $(LUA_H)
|
32
examples/lua_REPL/README.md
Normal file
32
examples/lua_REPL/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
## Lua interactive interpreter
|
||||
|
||||
### About
|
||||
|
||||
This example shows how to run a [Lua](https://www.lua.org/) Read-Eval-Print loop.
|
||||
It works in a similar way to the lua shell that comes with your operating
|
||||
system's default lua installation.
|
||||
|
||||
|
||||
### How to run
|
||||
|
||||
Type `make all flash` to program your board. The lua interpreter communicates
|
||||
via UART (like the shell).
|
||||
|
||||
It is not recommended to use `make term` because the default RIOT terminal messes
|
||||
up the input and output and the REPL needs multi-line input. Instead, use something
|
||||
like `miniterm.py` from pyserial:
|
||||
|
||||
```
|
||||
miniterm.py --eol LF --echo /dev/ttyACM0 115200
|
||||
```
|
||||
|
||||
By default only some of the builtin modules are loaded, to preserve RAM. See
|
||||
the definition of `BARE_MINIMUM_MODS` in main.c.
|
||||
|
||||
### Using the interpreter
|
||||
|
||||
See the [Lua manual](https://www.lua.org/manual/5.3/) for the syntax of the language.
|
||||
|
||||
Each piece of single or multi-line input is compiled as a chunk and run. For this
|
||||
reason, issuing "local" definitions may not work as expected: the definitions
|
||||
will be local to that chunk only.
|
63
examples/lua_REPL/main.c
Normal file
63
examples/lua_REPL/main.c
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Freie Universität Berlin.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup examples
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Lua shell in RIOT
|
||||
*
|
||||
* @author Juan Carrano <j.carrano@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua_run.h"
|
||||
#include "lua_builtin.h"
|
||||
#include "repl.lua.h"
|
||||
|
||||
/* The basic interpreter+repl needs about 13k ram AT Minimum but we need more
|
||||
* memory in order to do interesting stuff.
|
||||
*/
|
||||
#define MAIN_LUA_MEM_SIZE (40000)
|
||||
|
||||
static char lua_memory[MAIN_LUA_MEM_SIZE] __attribute__ ((aligned(__BIGGEST_ALIGNMENT__)));
|
||||
|
||||
#define BARE_MINIMUM_MODS (LUAR_LOAD_BASE | LUAR_LOAD_IO | LUAR_LOAD_CORO | LUAR_LOAD_PACKAGE)
|
||||
|
||||
const struct lua_riot_builtin_lua _lua_riot_builtin_lua_table[] = {
|
||||
{ "repl", repl_lua, sizeof(repl_lua) }
|
||||
};
|
||||
|
||||
|
||||
const struct lua_riot_builtin_lua *const lua_riot_builtin_lua_table = _lua_riot_builtin_lua_table;
|
||||
|
||||
const size_t lua_riot_builtin_lua_table_len = 1;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("Using memory range for Lua heap: %p - %p, %zu bytes\n",
|
||||
lua_memory, lua_memory + MAIN_LUA_MEM_SIZE, sizeof(void *));
|
||||
|
||||
while (1) {
|
||||
int status, value;
|
||||
puts("This is Lua: starting interactive session\n");
|
||||
|
||||
status = lua_riot_do_module("repl", lua_memory, MAIN_LUA_MEM_SIZE,
|
||||
BARE_MINIMUM_MODS, &value);
|
||||
|
||||
printf("Exited. status: %s, return code %d\n", lua_riot_strerror(status),
|
||||
value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
94
examples/lua_REPL/repl.lua
Normal file
94
examples/lua_REPL/repl.lua
Normal file
@ -0,0 +1,94 @@
|
||||
--[[
|
||||
@file repl.lua
|
||||
@brief Read-eval-print loop for LUA
|
||||
@author Juan Carrano <j.carrano@fu-berlin.de>
|
||||
Copyright (C) 2018 Freie Universität Berlin. Distributed under the GNU Lesser General Public License v2.1.
|
||||
]]
|
||||
|
||||
local _R_EVAL = 0
|
||||
local _R_CONT = 1
|
||||
local _R_EXIT = 2
|
||||
local _R_ERROR = 3
|
||||
|
||||
--[[ Read code from standard input (whatever stdin means for lua)
|
||||
@return action_code what the eval loop should do
|
||||
@return code_or_msg either code (_R_EVAL) or a message (_R_ERROR) or nil
|
||||
(_R_CONT, _R_EXIT).
|
||||
]]
|
||||
|
||||
local function re()
|
||||
io.write("L> ")
|
||||
io.flush()
|
||||
local ln = io.read()
|
||||
|
||||
if not ln then
|
||||
return _R_EXIT
|
||||
elseif ln == "\n" then
|
||||
return _R_CONT
|
||||
end
|
||||
-- Try to see if we have an expression
|
||||
local maybe_code, compile_err = load("return "..ln)
|
||||
-- Try to see if we have a single-line statement
|
||||
if not maybe_code then
|
||||
maybe_code, compile_err = load(ln)
|
||||
end
|
||||
-- Try a multiline statement
|
||||
if not maybe_code then
|
||||
-- It's not really necessary to use a coroutine, but it shows that they
|
||||
-- work.
|
||||
local _get_multiline = coroutine.create(
|
||||
function ()
|
||||
coroutine.yield(ln.."\n") -- We already have the first line of input
|
||||
while 1 do
|
||||
io.write("L.. ")
|
||||
io.flush()
|
||||
local l = io.read()
|
||||
if #l ~= 0 then
|
||||
l = l.."\n"
|
||||
end
|
||||
coroutine.yield(l)
|
||||
end
|
||||
end
|
||||
)
|
||||
local get_multiline = function()
|
||||
local a, b = coroutine.resume(_get_multiline)
|
||||
if a then
|
||||
return b
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
maybe_code, compile_err = load(get_multiline)
|
||||
end
|
||||
|
||||
if not maybe_code then
|
||||
return _R_ERROR, compile_err
|
||||
else
|
||||
return _R_EVAL, maybe_code
|
||||
end
|
||||
end
|
||||
|
||||
local function repl()
|
||||
io.write("Welcome to the interactive interpreter\n");
|
||||
|
||||
while 1 do
|
||||
local action, fn_or_message = re()
|
||||
|
||||
if action == _R_EVAL then
|
||||
local success, msg_or_ret = pcall(fn_or_message)
|
||||
if not success then
|
||||
print("Runtime error", msg_or_ret)
|
||||
elseif msg_or_ret ~= nil then
|
||||
print(msg_or_ret)
|
||||
end
|
||||
elseif action == _R_EXIT then
|
||||
print()
|
||||
return
|
||||
elseif action == _R_ERROR then
|
||||
print("Compile error:", fn_or_message)
|
||||
end -- (action == _R_CONT) : do nothing
|
||||
end
|
||||
end
|
||||
|
||||
repl()
|
Loading…
Reference in New Issue
Block a user