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