ulisp/ulisp-extensions.ino

494 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
User Extensions
*/
// Utility functions
uint8_t
dec_to_bcd(uint8_t n)
{
uint8_t bcd = 0;
uint8_t tens = n / 10;
bcd = tens << 4;
tens *= 10;
bcd += (n - tens) & 0x0f;
return bcd;
}
uint8_t
bcd_to_dec(uint8_t n)
{
return ((n>>4) * 10) + (n&0x0f);
}
/*
* Standard definitions
*
* These definitions should be the same on every platform.
*/
object *
fn_platform(object *args, object *env)
{
(void) args;
(void) env;
#if defined(PLATFORM_PICOCALC)
const char platformName[] = ":picocalc";
#elif defined(TDECK_PERI_POWERON) /* first t-deck define */
const char platformName[] = ":t-deck";
#elif defined(ARDUINO_TEENSY41)
const char platformName[] = ":teensy41";
#else
const char platformName[] = ":unknown";
#endif
return internlong((char *)platformName);
}
object *
fn_bcd_to_dec(object *args, object *env)
{
(void) env;
object *arg = car(args);
int n = checkinteger(arg);
if ((n < 0) || (n > 153)) {
error2("number not in the range [0...#x99].");
}
uint8_t result = bcd_to_dec(static_cast<uint8_t>(n));
return number(static_cast<int>(result));
}
object *
fn_dec_to_bcd(object *args, object *env)
{
(void) env;
object *arg = car(args);
int n = checkinteger(arg);
if ((n < 0) || (n > 99)) {
error2("number not in the range [0...99].");
}
uint8_t result = dec_to_bcd(static_cast<uint8_t>(n));
return number(static_cast<int>(result));
}
object *
fn_now(object *args, object *env)
{
(void) env;
static unsigned long Offset;
unsigned long now = millis()/1000;
int nargs = listlength(args);
// Set time
if (nargs == 3) {
Offset = (unsigned long)((checkinteger(first(args))*60 \
+ checkinteger(second(args)))*60 \
+ checkinteger(third(args)) - now);
} else if (nargs > 0) {
error2(PSTR("wrong number of arguments"));
}
// Return time
unsigned long secs = Offset + now;
object *seconds = number(secs%60);
object *minutes = number((secs/60)%60);
object *hours = number((secs/3600)%24);
return cons(hours, cons(minutes, cons(seconds, NULL)));
}
void
hyperprint(object *form, int lm, pfun_t pfun)
{
if (atom(form)) {
if (isbuiltin(form, NOTHING)) {
printsymbol(form, pfun);
} else {
printobject(form, pfun);
}
} else if (quoted(form)) {
pfun('\'');
hyperprint(car(cdr(form)), lm + 1, pfun);
} else {
lm = lm + PPINDENT;
bool fits = (subwidth(form, PPWIDTH - lm - PPINDENT) >= 0);
int special = 0, extra = 0; bool separate = true;
object *arg = car(form);
if (symbolp(arg) && builtinp(arg->name)) {
uint8_t minmax = getminmax(builtin(arg->name));
if (minmax == 0327 || minmax == 0313) {
special = 2; // defun, setq, setf, defvar
} else if (minmax == 0317 || minmax == 0017 ||
minmax == 0117 || minmax == 0123) {
special = 1;
}
}
while (form != NULL) {
if (atom(form)) {
pfstring(PSTR(" . "), pfun);
printobject(form, pfun);
pfun(')');
return;
} else if (separate) {
pfun('(');
separate = false;
} else if (special) {
pfun(' ');
special--;
} else if (fits) {
pfun(' ');
} else {
pln(pfun);
indent(lm, ' ', pfun);
}
hyperprint(car(form), lm+extra, pfun);
form = cdr(form);
}
pfun(')');
}
}
object *
fn_sym_def(object *args, object *env)
{
(void) env;
object *obj = first(args);
pfun_t pfun = pstreamfun(cdr(args));
#if defined(gfxsupport)
if (pfun == gfxwrite) ppwidth = GFXPPWIDTH;
#endif
object *pair = findvalue(obj, env);
object *var = car(pair);
object *val = cdr(pair);
pln(pfun);
if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) {
hyperprint(cons(bsymbol(DEFUN), cons(var, cdr(val))), 0, pfun);
} else {
hyperprint(cons(bsymbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun);
}
pln(pfun);
ppwidth = PPWIDTH;
return bsymbol(NOTHING);
}
object *
fn_listlibrary2(object *args, object *env)
{
(void) args, (void) env;
object *lst = nil;
GlobalStringIndex = 0;
object *line = read(glibrary);
while (line != NULL) {
builtin_t bname = builtin(first(line)->name);
if (bname == DEFUN || bname == DEFVAR) {
lst = cons(second(line), lst);
}
line = read(glibrary);
}
return lst;
}
object *
fn_lambdap(object *arg, object *env)
{
(void) env;
if (consp(arg)) {
arg = car(arg);
}
if (builtin(arg->name) == LAMBDA) {
return tee;
}
return nil;
}
// SD card standard library.
#if defined(sdcardsupport)
object *
fn_sd_rename(object *args, object *env)
{
(void) env;
char buffer1[BUFFERSIZE];
char buffer2[BUFFERSIZE];
object *pathFrom = car(args);
if (!stringp(pathFrom)) {
error2("filenames must be strings.");
}
object *pathTo = car(cdr(args));
if (!stringp(pathTo)) {
error2("filenames must be strings.");
}
if (!SD.rename((const char *)MakeFilename(pathFrom, buffer1),
(const char *)MakeFilename(pathTo, buffer2))) {
return nil;
}
return tee;
}
object *
fn_sd_remove(object *args, object *env)
{
(void) env;
char buffer[BUFFERSIZE];
object *arg = car(args);
if (!SD.remove(MakeFilename(arg, buffer))) {
return nil;
}
return tee;
}
object *
fn_sd_existsp(object *args, object *env)
{
(void) env;
char buffer[BUFFERSIZE];
object *arg = car(args);
if (!SD.exists(MakeFilename(arg, buffer))) {
return nil;
}
return tee;
}
/*
(sd-make-dir path)
Create a directory on the SD card.
This will also create any intermediate directories that dont already exists;
e.g. SD.mkdir("a/b/c") will create a, b, and c.
*/
object *
fn_sd_mkdir(object *args, object *env)
{
(void) env;
char buffer[BUFFERSIZE];
object *arg = car(args);
if (SD.mkdir(MakeFilename(arg, buffer))) {
return tee;
}
return nil;
}
/*
(sd-remove-dir path)
Remove a directory from the SD card. The directory must be empty.
*/
object *
fn_sd_rmdir(object *args, object *env)
{
(void) env;
char buffer[BUFFERSIZE];
object *arg = car(args);
if (SD.rmdir(MakeFilename(arg, buffer))) {
return tee;
}
return nil;
}
/*
* (sd-list)
*/
object *
fn_sd_list(object *args, object *env)
{
(void) env;
char *sd_path_buf = NULL;
SDBegin();
File root;
object *result = cons(NULL, NULL);
object *ptr = result;
if (args != NULL) {
object *arg1 = checkstring(first(args));
int len = stringlength(arg1) + 2; //make it longer for the initial slash and the null terminator
sd_path_buf = (char*)malloc(len);
if (sd_path_buf != NULL) {
cstring(arg1, &sd_path_buf[1], len-1);
sd_path_buf[0] = '/'; //really weird way to add a slash at the front...
root = SD.open(sd_path_buf);
}
} else{
root = SD.open("/");
}
while (true) {
File entry = root.openNextFile();
if (!entry) {
break; // no more files
}
object *filename = lispstring((char*)entry.name());
if (entry.isDirectory()) {
cdr(ptr) = cons(filename, NULL);
} else{
cdr(ptr) = cons(cons(filename, number(entry.size())), NULL);
}
ptr = cdr(ptr);
entry.close();
}
if (sd_path_buf != NULL) {
free(sd_path_buf);
}
root.close();
return cdr(result);
}
#endif
/*
* SYMBOL NAMES
*
* Define symbol names as const char[] PROGMEM here.
*/
const char stringlambdap[] PROGMEM = "lambdap";
const char stringnow[] PROGMEM = "now";
const char string_sym_def[] PROGMEM = "symdef";
const char stringbcd_to_dec[] PROGMEM = "bcd-to-dec";
const char stringdec_to_bcd[] PROGMEM = "dec-to-bcd";
const char stringlist_library2[] PROGMEM = "list-library2";
const char stringplatform[] PROGMEM = "platform";
#if defined(sdcardsupport)
const char stringsd_rename[] PROGMEM = "sd-rename";
const char stringsd_remove[] PROGMEM = "sd-remove";
const char stringsd_existsp[] PROGMEM = "sd-exists-p";
const char stringsd_mkdir[] PROGMEM = "sd-make-dir";
const char stringsd_rmdir[] PROGMEM = "sd-remove-dir";
const char stringsd_dir[] PROGMEM = "sd-list";
#endif
/*
* DOCUMENTATION STRINGS
*
* Define documentation strings as const char[] PROGMEM here.
*/
const char doclambdap[] PROGMEM = "(lambdap x)"
"Returns t if the form passed in is a lambda.";
const char docnow[] PROGMEM = "(now [hh mm ss])\n"
"Sets the current time, or with no arguments returns the current time\n"
"as a list of three integers (hh mm ss).";
const char doc_sym_def[] PROGMEM = "(symbol-def symbol [str])\n"
"Prints the definition of a symbol (variable or function) defined in\n"
"ulisp using the pretty printer."
"If str is specified it prints to the specified stream.\n"
"It returns no value.";
const char doc_bcd_to_dec[] PROGMEM = "(bcd-to-dec n)\n"
"Convert the BCD-encoded number n to decimal.";
const char doc_dec_to_bcd[] PROGMEM = "(dec-to-bcd n)\n"
"BCD encode n.";
const char doc_list_library2[] PROGMEM = "(list-library2)\n"
"Return a list of all symbols in the current Lisp library.";
const char doc_platform[] PROGMEM = "(platform)\n"
"Returns a keyword with the current platform. Supports :picocalc,\n"
":t-deck, and :teensy41. Otherwise, returns :unknown.";
#if defined(sdcardsupport)
const char docsd_rename[] PROGMEM = "(sd-rename from to)\n"
"Renames the file named by 'from' to 'to.'";
const char docsd_remove[] PROGMEM = "(sd-remove file)\n"
"Removes the named file from the filesystem. Returns t if the file\n"
"was remove successfully.";
const char docsd_existsp[] PROGMEM = "(sd-exists-p file)\n"
"Returns t if the named file exists.";
const char docsd_dir[] PROGMEM = "(sd-list [directory])\n"
"Returns a list of filenames in the root or named directory.";
const char docsd_mkdir[] PROGMEM = "(sd-make-dir directory)\n"
"Create a directory with the specified name. Returns t if \n"
"successful, otherwise nil.";
const char docsd_rmdir[] PROGMEM = "(sd-remove-dir directory)\n"
"Remove the named directory. Returns t if successful, otherwise nil.";
#endif
// Symbol lookup table
const tbl_entry_t lookup_table2[] PROGMEM = {
{ stringlambdap, fn_lambdap, 0211, doclambdap },
{ stringnow, fn_now, 0203, docnow },
{ string_sym_def, fn_sym_def, 0212, doc_sym_def },
{ stringbcd_to_dec, fn_bcd_to_dec, 0211, doc_bcd_to_dec },
{ stringdec_to_bcd, fn_dec_to_bcd, 0211, doc_dec_to_bcd },
{ stringlist_library2, fn_listlibrary2, 0200, doc_list_library2 },
{ stringplatform, fn_platform, 0200, doc_platform },
#if defined(sdcardsupport)
{ stringsd_rename, fn_sd_rename, 0222, docsd_rename },
{ stringsd_remove, fn_sd_remove, 0211, docsd_remove },
{ stringsd_existsp, fn_sd_existsp, 0211, docsd_existsp },
{ stringsd_dir, fn_sd_list, 0201, docsd_dir },
{ stringsd_mkdir, fn_sd_mkdir, 0211, docsd_mkdir },
{ stringsd_rmdir, fn_sd_rmdir, 0211, docsd_rmdir },
#endif
};
// Table cross-reference functions
tbl_entry_t *tables[] = {lookup_table, lookup_table2};
const unsigned int tablesizes[] = { arraysize(lookup_table), arraysize(lookup_table2) };
const tbl_entry_t *
table(int n)
{
return tables[n];
}
unsigned int
tablesize(int n)
{
return tablesizes[n];
}