10586 lines
309 KiB
C++
10586 lines
309 KiB
C++
/* uLisp ARM Release 4.7b - www.ulisp.com
|
|
David Johnson-Davies - www.technoblogy.com - 20th March 2025
|
|
|
|
Licensed under the MIT license: https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
// Lisp Library
|
|
// Loaded from LispLibrary.h
|
|
// const char LispLibrary[] PROGMEM = "";
|
|
|
|
// Compile options
|
|
|
|
#define resetautorun
|
|
#define printfreespace
|
|
// #define printgcs
|
|
#define sdcardsupport
|
|
#define gfxsupport
|
|
#define lisplibrary
|
|
#define assemblerlist
|
|
#define lineeditor
|
|
#define vt100
|
|
#define extensions
|
|
|
|
#define PLATFORM_PICOCALC 1
|
|
#define i2ckbd
|
|
#define BAUDRATE 96
|
|
|
|
// Includes
|
|
|
|
#include "LispLibrary.h"
|
|
|
|
#include <setjmp.h>
|
|
#include <SPI.h>
|
|
#include <Wire.h>
|
|
#include <limits.h>
|
|
|
|
#if defined(ARDUINO_RASPBERRY_PI_PICO) || \
|
|
defined(ARDUINO_RASPBERRY_PI_PICO_2W) || \
|
|
defined(ARDUINO_RASPBERRY_PI_PICO_W) || \
|
|
defined(ARDUINO_RASPBERRY_PI_PICO_2)
|
|
#define RASPBERRY_PI_PICO_PLATFORM
|
|
#if defined(PLATFORM_PICOCALC)
|
|
#define IS_PICOCALC
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(sdcardsupport)
|
|
#if defined(RASPBERRY_PI_PICO_PLATFORM)
|
|
#include <SDFS.h>
|
|
#endif
|
|
#include <SD.h>
|
|
#define SDSIZE 720
|
|
#else
|
|
#define SDSIZE 0
|
|
#endif
|
|
|
|
// Platform specific settings
|
|
|
|
#define WORDALIGNED __attribute__((aligned (4)))
|
|
#define BUFFERSIZE 36 // Number of bits+4
|
|
#define RAMFUNC __attribute__ ((section (".ramfunctions")))
|
|
#define MEMBANK
|
|
|
|
// ATSAMD21 boards ***************************************************************
|
|
|
|
#if defined(ARDUINO_GEMMA_M0) || defined(ARDUINO_SEEED_XIAO_M0) || defined(ARDUINO_QTPY_M0)
|
|
#define WORKSPACESIZE (2816-SDSIZE) /* Objects (8*bytes) */
|
|
#define CPUFLASH
|
|
#define FLASHSIZE 32768 /* Bytes */
|
|
#define CODESIZE 128 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define CPU_ATSAMD21
|
|
|
|
#elif defined(ARDUINO_ITSYBITSY_M0) || defined(ARDUINO_SAMD_FEATHER_M0_EXPRESS)
|
|
#define WORKSPACESIZE (2816-SDSIZE) /* Objects (8*bytes) */
|
|
#define DATAFLASH
|
|
#define FLASHSIZE 2048000 /* 2 MBytes */
|
|
#define CODESIZE 128 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define SDCARD_SS_PIN 4
|
|
#define CPU_ATSAMD21
|
|
|
|
#elif defined(ADAFRUIT_FEATHER_M0) /* Feather M0 without DataFlash */
|
|
#define WORKSPACESIZE (2816-SDSIZE) /* Objects (8*bytes) */
|
|
#define CPUFLASH
|
|
#define FLASHSIZE 32768 /* Bytes */
|
|
#define CODESIZE 128 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define SDCARD_SS_PIN 4
|
|
#define CPU_ATSAMD21
|
|
|
|
#elif defined(ARDUINO_SAMD_MKRZERO)
|
|
#define WORKSPACESIZE (2640-SDSIZE) /* Objects (8*bytes) */
|
|
#define CPUFLASH
|
|
#define FLASHSIZE 32768 /* Bytes */
|
|
#define CODESIZE 128 /* Bytes */
|
|
#define STACKDIFF 840
|
|
#define CPU_ATSAMD21
|
|
|
|
#elif defined(ARDUINO_SAMD_ZERO) /* Put this last, otherwise overrides the Adafruit boards */
|
|
#define WORKSPACESIZE (2500-SDSIZE) /* Objects (8*bytes) */
|
|
#define CPUFLASH
|
|
#define FLASHSIZE 32768 /* Bytes */
|
|
#define CODESIZE 128 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define SDCARD_SS_PIN 10
|
|
#define CPU_ATSAMD21
|
|
|
|
// ATSAMD51 boards ***************************************************************
|
|
|
|
#elif defined(ARDUINO_METRO_M4) || defined(ARDUINO_ITSYBITSY_M4) || defined(ARDUINO_FEATHER_M4)
|
|
#define WORKSPACESIZE (20608-SDSIZE) /* Objects (8*bytes) */
|
|
#define DATAFLASH
|
|
#define FLASHSIZE 2048000 /* 2 MBytes */
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 400
|
|
#define SDCARD_SS_PIN 10
|
|
#define CPU_ATSAMD51
|
|
|
|
#elif defined(ARDUINO_PYBADGE_M4) || defined(ARDUINO_PYGAMER_M4)
|
|
#define WORKSPACESIZE (20608-SDSIZE) /* Objects (8*bytes) */
|
|
#define DATAFLASH
|
|
#define FLASHSIZE 2048000 /* 2 MBytes */
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 400
|
|
#define SDCARD_SS_PIN 10
|
|
#define CPU_ATSAMD51
|
|
#if defined(gfxsupport)
|
|
const int TFT_BACKLIGHT = 47;
|
|
#include <Adafruit_GFX.h> // Core graphics library
|
|
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
|
|
Adafruit_ST7735 tft = Adafruit_ST7735(44, 45, 41, 42, 46);
|
|
#endif
|
|
|
|
#elif defined(ARDUINO_WIO_TERMINAL)
|
|
#define WORKSPACESIZE (20480-SDSIZE) /* Objects (8*bytes) */
|
|
#define DATAFLASH
|
|
#define FLASHSIZE 2048000 /* 2 MBytes */
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 400
|
|
#define CPU_ATSAMD51
|
|
#define EXTERNAL_FLASH_USE_QSPI
|
|
#if defined(gfxsupport)
|
|
#include <TFT_eSPI.h> // Hardware-specific library
|
|
TFT_eSPI tft = TFT_eSPI();
|
|
#endif
|
|
|
|
#elif defined(ARDUINO_GRAND_CENTRAL_M4)
|
|
#define WORKSPACESIZE (28800-SDSIZE) /* Objects (8*bytes) */
|
|
#define DATAFLASH
|
|
#define FLASHSIZE 8192000 /* 8 MBytes */
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 440
|
|
#define CPU_ATSAMD51
|
|
|
|
// nRF51 boards ***************************************************************
|
|
|
|
#elif defined(ARDUINO_BBC_MICROBIT) || defined(ARDUINO_SINOBIT)
|
|
#define WORKSPACESIZE 1344 /* Objects (8*bytes) */
|
|
#define CODESIZE 64 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define CPU_NRF51822
|
|
|
|
#elif defined(ARDUINO_CALLIOPE_MINI)
|
|
#define WORKSPACESIZE 3392 /* Objects (8*bytes) */
|
|
#define CODESIZE 64 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define CPU_NRF51822
|
|
|
|
// nRF52 boards ***************************************************************
|
|
|
|
#elif defined(ARDUINO_BBC_MICROBIT_V2)
|
|
#define WORKSPACESIZE 12928 /* Objects (8*bytes) */
|
|
#define CODESIZE 128 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define CPU_NRF52833
|
|
|
|
#elif defined(ARDUINO_NRF52840_ITSYBITSY) || defined(ARDUINO_Seeed_XIAO_nRF52840) \
|
|
|| defined(ARDUINO_Seeed_XIAO_nRF52840_Sense) || defined(ARDUINO_NRF52840_CIRCUITPLAY)
|
|
#define WORKSPACESIZE (21120-SDSIZE) /* Objects (8*bytes) */
|
|
#define DATAFLASH
|
|
#define FLASHSIZE 2048000 /* 2 MBytes */
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 8
|
|
#define CPU_NRF52840
|
|
|
|
#elif defined(ARDUINO_NRF52840_CLUE)
|
|
#define WORKSPACESIZE (21120-SDSIZE) /* Objects (8*bytes) */
|
|
#define DATAFLASH
|
|
#define FLASHSIZE 2048000 /* 2 MBytes */
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 8
|
|
#define CPU_NRF52840
|
|
#if defined(gfxsupport)
|
|
#include <Adafruit_GFX.h>
|
|
#include <Adafruit_ST7789.h>
|
|
Adafruit_ST7789 tft = Adafruit_ST7789(&SPI1, PIN_TFT_CS, PIN_TFT_DC, PIN_TFT_RST);
|
|
#endif
|
|
|
|
// MAX32620 boards ***************************************************************
|
|
|
|
#elif defined(MAX32620)
|
|
#define WORKSPACESIZE (24704-SDSIZE) /* Objects (8*bytes) */
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define CPU_MAX32620
|
|
#define Wire1 Wire2
|
|
|
|
// iMXRT1062 boards ***************************************************************
|
|
|
|
#elif defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)
|
|
#define WORKSPACESIZE 60000 /* Objects (8*bytes) */
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 15000
|
|
#define LITTLEFS (960 * 1024)
|
|
#include <LittleFS.h>
|
|
LittleFS_Program LittleFS;
|
|
#define FS_FILE_WRITE FILE_WRITE_BEGIN
|
|
#define FS_FILE_READ FILE_READ
|
|
#define CPU_iMXRT1062
|
|
#define SDCARD_SS_PIN BUILTIN_SDCARD
|
|
#define BitOrder uint8_t
|
|
#undef RAMFUNC
|
|
#define RAMFUNC FASTRUN
|
|
#undef MEMBANK
|
|
#define MEMBANK DMAMEM
|
|
|
|
// RP2040 boards ***************************************************************
|
|
|
|
#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) \
|
|
|| defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2040)
|
|
#define WORKSPACESIZE (23000-SDSIZE) /* Objects (8*bytes) */
|
|
|
|
#if defined(sdcardsupport)
|
|
#define SDCARD_SS_PIN 17
|
|
#else
|
|
#define LITTLEFS
|
|
#include <LittleFS.h>
|
|
#define FILE_WRITE_BEGIN "w"
|
|
#define FILE_READ "r"
|
|
#endif
|
|
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define CPU_RP2040
|
|
#if defined(gfxsupport)
|
|
#include <TFT_eSPI.h> // Hardware-specific library
|
|
#if defined(PLATFORM_PICOCALC)
|
|
#include <PCKeyboard.h>
|
|
PCKeyboard pc_kbd;
|
|
TFT_eSPI tft = TFT_eSPI(320,320);
|
|
#else
|
|
TFT_eSPI tft = TFT_eSPI();
|
|
#endif
|
|
#endif
|
|
|
|
#elif defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER)
|
|
#define WORKSPACESIZE 23000 /* Objects (8*bytes) */
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 480
|
|
#define LITTLEFS
|
|
#include <LittleFS.h>
|
|
#define FS_FILE_WRITE "w"
|
|
#define FS_FILE_READ "r"
|
|
#define SDCARD_SS_PIN 23
|
|
#define CPU_RP2040
|
|
|
|
#elif defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
|
#define WORKSPACESIZE (15232-SDSIZE) /* Objects (8*bytes) */
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define STACKDIFF 480
|
|
#define LITTLEFS
|
|
#include <WiFi.h>
|
|
#include <LittleFS.h>
|
|
#define FS_FILE_WRITE "w"
|
|
#define FS_FILE_READ "r"
|
|
#define CPU_RP2040
|
|
|
|
// RP2350 boards ***************************************************************
|
|
|
|
#elif defined(ARDUINO_RASPBERRY_PI_PICO_2)
|
|
#if defined(__riscv)
|
|
#define WORKSPACESIZE (42500-SDSIZE) /* Objects (8*bytes) */
|
|
#define STACKDIFF 580
|
|
#else
|
|
#define WORKSPACESIZE (47000-SDSIZE) /* Objects (8*bytes) */
|
|
#define STACKDIFF 520
|
|
#endif
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define LITTLEFS
|
|
#include <LittleFS.h>
|
|
#define FS_FILE_WRITE "w"
|
|
#define FS_FILE_READ "r"
|
|
#define CPU_RP2350
|
|
|
|
#elif defined(ARDUINO_RASPBERRY_PI_PICO_2W)
|
|
#if defined(__riscv)
|
|
#define WORKSPACESIZE (34850-SDSIZE) /* Objects (8*bytes) */
|
|
#define STACKDIFF 580
|
|
#else
|
|
#define WORKSPACESIZE (39200-SDSIZE) /* Objects (8*bytes) */
|
|
#define STACKDIFF 520
|
|
#endif
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define LITTLEFS
|
|
#include <WiFi.h>
|
|
// #include <LittleFS.h>
|
|
#if defined(sdcardsupport)
|
|
#define SDCARD_SS_PIN 17
|
|
#else
|
|
#define LITTLEFS
|
|
#include <LittleFS.h>
|
|
#define FILE_WRITE_BEGIN "w"
|
|
#define FILE_READ "r"
|
|
#endif
|
|
#define FS_FILE_WRITE "w"
|
|
#define FS_FILE_READ "r"
|
|
#define CPU_RP2350j
|
|
#if defined(gfxsupport)
|
|
#include <TFT_eSPI.h> // Hardware-specific library
|
|
#if defined(PLATFORM_PICOCALC)
|
|
#include <PCKeyboard.h>
|
|
PCKeyboard pc_kbd;
|
|
TFT_eSPI tft = TFT_eSPI(320,320);
|
|
#else
|
|
TFT_eSPI tft = TFT_eSPI();
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#elif defined(ARDUINO_PIMORONI_PICO_PLUS_2)
|
|
//#define BOARD_HAS_PSRAM /* Uncomment to use PSRAM */
|
|
#if defined(BOARD_HAS_PSRAM)
|
|
#undef MEMBANK
|
|
#define MEMBANK PSRAM
|
|
#define WORKSPACESIZE 1000000 /* Objects (8*bytes) */
|
|
#define STACKDIFF 580
|
|
#elif defined(__riscv)
|
|
#define WORKSPACESIZE (42000-SDSIZE) /* Objects (8*bytes) */
|
|
#define STACKDIFF 580
|
|
#else
|
|
#define WORKSPACESIZE (46500-SDSIZE) /* Objects (8*bytes) */
|
|
#define STACKDIFF 520
|
|
#endif
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define LITTLEFS
|
|
#include <LittleFS.h>
|
|
#define FS_FILE_WRITE "w"
|
|
#define FS_FILE_READ "r"
|
|
#define SDCARD_SS_PIN 10
|
|
#define CPU_RP2350
|
|
|
|
#elif defined(ARDUINO_PIMORONI_TINY2350)
|
|
#if defined(__riscv)
|
|
#define WORKSPACESIZE (42500-SDSIZE) /* Objects (8*bytes) */
|
|
#define STACKDIFF 580
|
|
#else
|
|
#define WORKSPACESIZE (47000-SDSIZE) /* Objects (8*bytes) */
|
|
#define STACKDIFF 520
|
|
#endif
|
|
#define CODESIZE 256 /* Bytes */
|
|
#define LITTLEFS
|
|
#include <LittleFS.h>
|
|
#define FS_FILE_WRITE "w"
|
|
#define FS_FILE_READ "r"
|
|
#define CPU_RP2350
|
|
|
|
// RA4M1 boards ***************************************************************
|
|
|
|
#elif defined(ARDUINO_MINIMA)
|
|
#define WORKSPACESIZE (2032-SDSIZE) /* Objects (8*bytes) */
|
|
#include <EEPROM.h>
|
|
#define EEPROMFLASH
|
|
#define FLASHSIZE 8192 /* Bytes */
|
|
#define CODESIZE 128 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define eAnalogReference ar_aref
|
|
#define CPU_RA4M1
|
|
#define SDCARD_SS_PIN 10
|
|
|
|
#elif defined(ARDUINO_UNOWIFIR4)
|
|
#define WORKSPACESIZE (1610-SDSIZE) /* Objects (8*bytes) */
|
|
#include <EEPROM.h>
|
|
#include "WiFiS3.h"
|
|
#define EEPROMFLASH
|
|
#define FLASHSIZE 8192 /* Bytes */
|
|
#define CODESIZE 128 /* Bytes */
|
|
#define STACKDIFF 320
|
|
#define eAnalogReference ar_aref
|
|
#define CPU_RA4M1
|
|
#define SDCARD_SS_PIN 10
|
|
|
|
#else
|
|
#error "Board not supported!"
|
|
#endif
|
|
|
|
#if defined(gfxsupport)
|
|
const int COLOR_WHITE = 0xffff;
|
|
const int COLOR_BLACK = 0;
|
|
const int COLOR_GREEN = 0x0600;
|
|
const int TEXT_COLOR = COLOR_GREEN;
|
|
const int BG_COLOR = COLOR_BLACK;
|
|
#endif
|
|
|
|
|
|
|
|
// C Macros
|
|
|
|
#define nil NULL
|
|
#define car(x) (((object *) (x))->car)
|
|
#define cdr(x) (((object *) (x))->cdr)
|
|
|
|
#define first(x) car(x)
|
|
#define rest(x) cdr(x)
|
|
#define second(x) first(rest(x))
|
|
#define cddr(x) cdr(cdr(x))
|
|
#define third(x) first(cddr(x))
|
|
|
|
#define push(x, y) ((y) = cons((x),(y)))
|
|
#define pop(y) ((y) = cdr(y))
|
|
|
|
#define protect(y) push((y), GCStack)
|
|
#define unprotect() pop(GCStack)
|
|
|
|
#define integerp(x) ((x) != NULL && (x)->type == NUMBER)
|
|
#define floatp(x) ((x) != NULL && (x)->type == FLOAT)
|
|
#define symbolp(x) ((x) != NULL && (x)->type == SYMBOL)
|
|
#define stringp(x) ((x) != NULL && (x)->type == STRING)
|
|
#define characterp(x) ((x) != NULL && (x)->type == CHARACTER)
|
|
#define arrayp(x) ((x) != NULL && (x)->type == ARRAY)
|
|
#define streamp(x) ((x) != NULL && (x)->type == STREAM)
|
|
|
|
#define mark(x) (car(x) = (object *)(((uintptr_t)(car(x))) | MARKBIT))
|
|
#define unmark(x) (car(x) = (object *)(((uintptr_t)(car(x))) & ~MARKBIT))
|
|
#define marked(x) ((((uintptr_t)(car(x))) & MARKBIT) != 0)
|
|
#define MARKBIT 1
|
|
|
|
#define setflag(x) (Flags = Flags | 1<<(x))
|
|
#define clrflag(x) (Flags = Flags & ~(1<<(x)))
|
|
#define tstflag(x) (Flags & 1<<(x))
|
|
|
|
#define issp(x) (x == ' ' || x == '\n' || x == '\r' || x == '\t')
|
|
#define isbr(x) (x == ')' || x == '(' || x == '"' || x == '#' || x == '\'')
|
|
#define fntype(x) (getminmax((uint16_t)(x))>>6)
|
|
#define longsymbolp(x) (((x)->name & 0x03) == 0)
|
|
#define longnamep(x) (((x) & 0x03) == 0)
|
|
#define twist(x) ((uint32_t)((x)<<2) | (((x) & 0xC0000000)>>30))
|
|
#define untwist(x) (((x)>>2 & 0x3FFFFFFF) | ((x) & 0x03)<<30)
|
|
#define arraysize(x) (sizeof(x) / sizeof(x[0]))
|
|
#define stringifyX(x) #x
|
|
#define stringify(x) stringifyX(x)
|
|
#define PACKEDS 0x43238000
|
|
#define BUILTINS 0xF4240000
|
|
#define ENDFUNCTIONS 0x0BDC0000
|
|
|
|
// Code marker stores start and end of code block
|
|
#define startblock(x) ((x->integer) & 0xFFFF)
|
|
#define endblock(x) ((x->integer) >> 16 & 0xFFFF)
|
|
|
|
// Constants
|
|
|
|
#define TRACEMAX 3 // Maximum number of traced functions
|
|
enum type { ZZERO=0, SYMBOL=2, CODE=4, NUMBER=6, STREAM=8, CHARACTER=10, FLOAT=12, ARRAY=14, STRING=16, PAIR=18 }; // ARRAY STRING and PAIR must be last
|
|
enum token { UNUSED, BRA, KET, QUO, DOT };
|
|
enum stream { SERIALSTREAM, I2CSTREAM, SPISTREAM, SDSTREAM, WIFISTREAM, STRINGSTREAM, GFXSTREAM };
|
|
enum fntypes_t { OTHER_FORMS, TAIL_FORMS, FUNCTIONS, SPECIAL_FORMS };
|
|
|
|
// Stream names used by printobject
|
|
const char serialstream[] = "serial";
|
|
const char i2cstream[] = "i2c";
|
|
const char spistream[] = "spi";
|
|
const char sdstream[] = "sd";
|
|
const char wifistream[] = "wifi";
|
|
const char stringstream[] = "string";
|
|
const char gfxstream[] = "gfx";
|
|
const char *const streamname[] = {serialstream, i2cstream, spistream, sdstream, wifistream, stringstream, gfxstream};
|
|
|
|
// Typedefs
|
|
|
|
typedef uint32_t symbol_t;
|
|
typedef uint32_t builtin_t;
|
|
typedef uint32_t chars_t;
|
|
|
|
typedef struct sobject {
|
|
union {
|
|
struct {
|
|
sobject *car;
|
|
sobject *cdr;
|
|
};
|
|
struct {
|
|
unsigned int type;
|
|
union {
|
|
symbol_t name;
|
|
int integer;
|
|
chars_t chars; // For strings
|
|
float single_float;
|
|
};
|
|
};
|
|
};
|
|
} object;
|
|
|
|
typedef object *(*fn_ptr_type)(object *, object *);
|
|
typedef void (*mapfun_t)(object *, object **);
|
|
typedef int (*intfn_ptr_type)(int w, int x, int y, int z);
|
|
|
|
typedef const struct {
|
|
const char *string;
|
|
fn_ptr_type fptr;
|
|
uint8_t minmax;
|
|
const char *doc;
|
|
} tbl_entry_t;
|
|
|
|
typedef int (*gfun_t)();
|
|
typedef void (*pfun_t)(char);
|
|
|
|
enum builtins: builtin_t { NIL, TEE, NOTHING, OPTIONAL, FEATURES, INITIALELEMENT, ELEMENTTYPE, TEST, COLONA, COLONB,
|
|
COLONC, BIT, AMPREST, LAMBDA, LET, LETSTAR, CLOSURE, PSTAR, QUOTE, DEFUN, DEFVAR, DEFCODE, EQ, CAR, FIRST,
|
|
CDR, REST, NTH, AREF, CHAR, STRINGFN, PINMODE, DIGITALWRITE, ANALOGREAD, ANALOGREFERENCE, REGISTER,
|
|
FORMAT,
|
|
};
|
|
|
|
// Global variables
|
|
HardwareSerial *seriell;//pointer to which serial to use according the real board type
|
|
|
|
object Workspace[WORKSPACESIZE] WORDALIGNED MEMBANK;
|
|
#if defined(CODESIZE)
|
|
RAMFUNC uint8_t MyCode[CODESIZE] WORDALIGNED;
|
|
#endif
|
|
|
|
jmp_buf toplevel_handler;
|
|
jmp_buf *handler = &toplevel_handler;
|
|
unsigned int Freespace = 0;
|
|
object *Freelist;
|
|
unsigned int I2Ccount;
|
|
unsigned int TraceFn[TRACEMAX];
|
|
unsigned int TraceDepth[TRACEMAX];
|
|
builtin_t Context;
|
|
#define BACKTRACESIZE 8
|
|
uint8_t TraceStart = 0, TraceTop = 0;
|
|
symbol_t Backtrace[BACKTRACESIZE];
|
|
|
|
object *GlobalEnv;
|
|
object *GCStack = NULL;
|
|
object *GlobalString;
|
|
object *GlobalStringTail;
|
|
int GlobalStringIndex = 0;
|
|
uint8_t PrintCount = 0;
|
|
uint8_t BreakLevel = 0;
|
|
char LastChar = 0;
|
|
char LastPrint = 0;
|
|
|
|
// Flags
|
|
enum flag { PRINTREADABLY, RETURNFLAG, ESCAPE, EXITEDITOR, LIBRARYLOADED, NOESC, NOECHO, MUFFLEERRORS, BACKTRACE };
|
|
typedef uint16_t flags_t;
|
|
volatile flags_t Flags = 1<<PRINTREADABLY; // Set by default
|
|
|
|
// Forward references
|
|
object *tee;
|
|
void pfstring (const char *s, pfun_t pfun);
|
|
|
|
// Error handling
|
|
|
|
int modbacktrace (int n) {
|
|
return (n+BACKTRACESIZE) % BACKTRACESIZE;
|
|
}
|
|
|
|
/*
|
|
printbacktrace - prints a call backtrace for error messages and break.
|
|
*/
|
|
void
|
|
printbacktrace ()
|
|
{
|
|
if (TraceStart != TraceTop) {
|
|
pserial('[');
|
|
}
|
|
|
|
int tracesize = modbacktrace(TraceTop-TraceStart);
|
|
|
|
for (int i=1; i<=tracesize; i++) {
|
|
printsymbol(symbol(Backtrace[modbacktrace(TraceTop-i)]), pserial);
|
|
if (i!=tracesize) {
|
|
pfstring(" <- ", pserial);
|
|
}
|
|
}
|
|
|
|
if (TraceStart != TraceTop) {
|
|
pserial(']');
|
|
}
|
|
}
|
|
|
|
/*
|
|
errorsub - used by all the error routines.
|
|
Prints: "Error: 'fname' string", where fname is the name of the
|
|
Lisp function in which the error occurred.
|
|
*/
|
|
void
|
|
errorsub(symbol_t fname, const char *string)
|
|
{
|
|
pfl(pserial); pfstring("Error", pserial);
|
|
|
|
if (TraceStart != TraceTop) {
|
|
pserial(' ');
|
|
}
|
|
|
|
printbacktrace();
|
|
pfstring(": ", pserial);
|
|
|
|
if (fname != sym(NIL)) {
|
|
pserial('\'');
|
|
psymbol(fname, pserial);
|
|
pserial('\''); pserial(' ');
|
|
}
|
|
|
|
pfstring(string, pserial);
|
|
}
|
|
|
|
void
|
|
errorend()
|
|
{
|
|
GCStack = NULL;
|
|
longjmp(*handler, 1);
|
|
}
|
|
|
|
/*
|
|
errorsym - prints an error message and reenters the REPL.
|
|
Prints: "Error: 'fname' string: symbol", where fname is the name
|
|
of the user Lisp function in which the error occurred,
|
|
and symbol is the object generating the error.
|
|
*/
|
|
void
|
|
errorsym(symbol_t fname, const char *string, object *symbol)
|
|
{
|
|
if (!tstflag(MUFFLEERRORS)) {
|
|
errorsub(fname, string);
|
|
pserial(':');
|
|
pserial(' ');
|
|
printobject(symbol, pserial);
|
|
pln(pserial);
|
|
}
|
|
|
|
errorend();
|
|
}
|
|
|
|
/*
|
|
errorsym2 - prints an error message and reenters the REPL.
|
|
Prints: "Error: 'fname' string", where fname is the name of the
|
|
user Lisp function in which the error occurred.
|
|
*/
|
|
void
|
|
errorsym2(symbol_t fname, const char *string)
|
|
{
|
|
if (!tstflag(MUFFLEERRORS)) {
|
|
errorsub(fname, string);
|
|
pln(pserial);
|
|
}
|
|
|
|
errorend();
|
|
}
|
|
|
|
/*
|
|
error - prints an error message and reenters the REPL.
|
|
Prints: "Error: 'Context' string: symbol", where Context is the
|
|
name of the built-in Lisp function in which the error occurred,
|
|
and symbol is the object generating the error.
|
|
*/
|
|
void
|
|
error(const char *string, object *symbol)
|
|
{
|
|
errorsym(sym(Context), string, symbol);
|
|
}
|
|
|
|
/*
|
|
error2 - prints an error message and reenters the REPL.
|
|
Prints: "Error: 'Context' string", where Context is the name of
|
|
the built-in Lisp function in which the error occurred.
|
|
*/
|
|
void
|
|
error2(const char *string)
|
|
{
|
|
errorsym2(sym(Context), string);
|
|
}
|
|
|
|
/*
|
|
formaterr - displays a format error with a ^ pointing to the error
|
|
*/
|
|
void
|
|
formaterr(object *formatstr, const char *string, uint8_t p)
|
|
{
|
|
pln(pserial);
|
|
indent(4, ' ', pserial);
|
|
printstring(formatstr, pserial);
|
|
pln(pserial);
|
|
|
|
indent(p+5, ' ', pserial); pserial('^');
|
|
error2(string);
|
|
pln(pserial);
|
|
errorend();
|
|
}
|
|
|
|
// Save space as these are used multiple times
|
|
const char notanumber[] = "argument is not a number";
|
|
const char notaninteger[] = "argument is not an integer";
|
|
const char notastring[] = "argument is not a string";
|
|
const char notalist[] = "argument is not a list";
|
|
const char notasymbol[] = "argument is not a symbol";
|
|
const char notproper[] = "argument is not a proper list";
|
|
const char toomanyargs[] = "too many arguments";
|
|
const char toofewargs[] = "too few arguments";
|
|
const char noargument[] = "missing argument";
|
|
const char nostream[] = "missing stream argument";
|
|
const char overflow[] = "arithmetic overflow";
|
|
const char divisionbyzero[] = "division by zero";
|
|
const char indexnegative[] = "index can't be negative";
|
|
const char invalidarg[] = "invalid argument";
|
|
const char invalidkey[] = "invalid keyword";
|
|
const char illegalclause[] = "illegal clause";
|
|
const char illegalfn[] = "illegal function";
|
|
const char invalidpin[] = "invalid pin";
|
|
const char oddargs[] = "odd number of arguments";
|
|
const char indexrange[] = "index out of range";
|
|
const char canttakecar[] = "can't take car";
|
|
const char canttakecdr[] = "can't take cdr";
|
|
const char unknownstreamtype[] = "unknown stream type";
|
|
|
|
|
|
// Set up workspace
|
|
|
|
/*
|
|
initworkspace - initialises the workspace into a linked list of free objects
|
|
*/
|
|
void
|
|
initworkspace()
|
|
{
|
|
Freelist = NULL;
|
|
|
|
for (int i=WORKSPACESIZE-1; i>=0; i--) {
|
|
object *obj = &Workspace[i];
|
|
car(obj) = NULL;
|
|
cdr(obj) = Freelist;
|
|
Freelist = obj;
|
|
Freespace++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
myalloc - returns the first object from the linked list of free objects
|
|
*/
|
|
object *
|
|
myalloc()
|
|
{
|
|
if (Freespace == 0) {
|
|
Context = NIL;
|
|
error2("no room");
|
|
}
|
|
|
|
object *temp = Freelist;
|
|
Freelist = cdr(Freelist);
|
|
Freespace--;
|
|
|
|
return temp;
|
|
}
|
|
|
|
/*
|
|
myfree - adds obj to the linked list of free objects.
|
|
inline makes gc significantly faster
|
|
*/
|
|
inline void
|
|
myfree(object *obj)
|
|
{
|
|
car(obj) = NULL;
|
|
cdr(obj) = Freelist;
|
|
Freelist = obj;
|
|
Freespace++;
|
|
}
|
|
|
|
// Make each type of object
|
|
|
|
/*
|
|
number - make an integer object with value n and return it
|
|
*/
|
|
object *
|
|
number(int n)
|
|
{
|
|
object *ptr = myalloc();
|
|
ptr->type = NUMBER;
|
|
ptr->integer = n;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
makefloat - make a floating point object with value f and return it
|
|
*/
|
|
object *
|
|
makefloat(float f)
|
|
{
|
|
object *ptr = myalloc();
|
|
ptr->type = FLOAT;
|
|
ptr->single_float = f;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
character - make a character object with value c and return it
|
|
*/
|
|
object *
|
|
character(uint8_t c)
|
|
{
|
|
object *ptr = myalloc();
|
|
ptr->type = CHARACTER;
|
|
ptr->chars = c;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
cons - make a cons with arg1 and arg2 return it
|
|
*/
|
|
object *
|
|
cons(object *arg1, object *arg2)
|
|
{
|
|
object *ptr = myalloc();
|
|
ptr->car = arg1;
|
|
ptr->cdr = arg2;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
symbol - make a symbol object with value name and return it
|
|
*/
|
|
object *
|
|
symbol(symbol_t name)
|
|
{
|
|
object *ptr = myalloc();
|
|
ptr->type = SYMBOL;
|
|
ptr->name = name;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
bsymbol - make a built-in symbol
|
|
*/
|
|
inline object *
|
|
bsymbol(builtin_t name)
|
|
{
|
|
return intern(twist(name+BUILTINS));
|
|
}
|
|
|
|
/*
|
|
codehead - make a code header object with value entry and return it
|
|
*/
|
|
object *
|
|
codehead(int entry)
|
|
{
|
|
object *ptr = myalloc();
|
|
ptr->type = CODE;
|
|
ptr->integer = entry;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
intern - unless PSRAM: looks through the workspace for an existing
|
|
occurrence of symbol name and returns it, otherwise calls
|
|
symbol(name) to create a new symbol.
|
|
*/
|
|
object *
|
|
intern (symbol_t name)
|
|
{
|
|
#if !defined(BOARD_HAS_PSRAM)
|
|
for (int i=0; i<WORKSPACESIZE; i++) {
|
|
object *obj = &Workspace[i];
|
|
|
|
if (obj->type == SYMBOL && obj->name == name) {
|
|
return obj;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return symbol(name);
|
|
}
|
|
|
|
/*
|
|
eqsymbols - compares the long string/symbol obj with the string in buffer.
|
|
*/
|
|
bool
|
|
eqsymbols(object *obj, char *buffer)
|
|
{
|
|
int i = 0;
|
|
object *arg = cdr(obj);
|
|
|
|
while (!(arg == NULL && buffer[i] == 0)) {
|
|
if (arg == NULL || buffer[i] == 0) {
|
|
return false;
|
|
}
|
|
|
|
chars_t test = 0;
|
|
int shift = 24;
|
|
|
|
for (int j=0; j<4; j++, i++) {
|
|
if (buffer[i] == 0) break;
|
|
test = test | buffer[i]<<shift;
|
|
shift = shift - 8;
|
|
}
|
|
|
|
if (arg->chars != test) {
|
|
return false;
|
|
}
|
|
|
|
arg = car(arg);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
internlong - unless PSRAM looks through the workspace for an
|
|
existing occurrence of the long symbol in buffer and returns it,
|
|
otherwise calls lispstring(buffer) to create a new symbol.
|
|
*/
|
|
object *
|
|
internlong (char *buffer)
|
|
{
|
|
#if !defined(BOARD_HAS_PSRAM)
|
|
for (int i=0; i<WORKSPACESIZE; i++) {
|
|
object *obj = &Workspace[i];
|
|
|
|
if (obj->type == SYMBOL && longsymbolp(obj) && eqsymbols(obj, buffer)) {
|
|
return obj;
|
|
}
|
|
}
|
|
#endif
|
|
object *obj = lispstring(buffer);
|
|
obj->type = SYMBOL;
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
stream - makes a stream object defined by streamtype and address,
|
|
and returns it
|
|
*/
|
|
object *
|
|
stream(uint8_t streamtype, uint8_t address)
|
|
{
|
|
object *ptr = myalloc();
|
|
ptr->type = STREAM;
|
|
ptr->integer = streamtype<<8 | address;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
newstring - makes an empty string object and returns it
|
|
*/
|
|
object *
|
|
newstring()
|
|
{
|
|
object *ptr = myalloc();
|
|
ptr->type = STRING;
|
|
ptr->chars = 0;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
// Features
|
|
|
|
const char floatingpoint[] = ":floating-point";
|
|
const char arrays[] = ":arrays";
|
|
const char doc[] = ":documentation";
|
|
const char machinecode[] = ":machine-code";
|
|
const char errorhandling[] = ":error-handling";
|
|
const char wifi[] = ":wi-fi";
|
|
const char gfx[] = ":gfx";
|
|
const char sdcard[] = ":sd-card";
|
|
const char arm[] = ":arm";
|
|
const char riscv[] = ":risc-v";
|
|
|
|
object *
|
|
features()
|
|
{
|
|
object *result = NULL;
|
|
#if defined(__riscv)
|
|
push(internlong((char *)riscv), result);
|
|
#else
|
|
push(internlong((char *)arm), result);
|
|
#endif
|
|
#if defined(sdcardsupport)
|
|
push(internlong((char *)sdcard), result);
|
|
#endif
|
|
push(internlong((char *)gfx), result);
|
|
push(internlong((char *)wifi), result);
|
|
push(internlong((char *)errorhandling), result);
|
|
push(internlong((char *)machinecode), result);
|
|
push(internlong((char *)doc), result);
|
|
push(internlong((char *)arrays), result);
|
|
push(internlong((char *)floatingpoint), result);
|
|
return result;
|
|
}
|
|
|
|
// Garbage collection
|
|
|
|
/*
|
|
markobject - recursively marks reachable objects, starting from obj
|
|
*/
|
|
void
|
|
markobject(object *obj)
|
|
{
|
|
MARK:
|
|
if (obj == NULL) return;
|
|
if (marked(obj)) return;
|
|
|
|
object* arg = car(obj);
|
|
unsigned int type = obj->type;
|
|
mark(obj);
|
|
|
|
if (type >= PAIR || type == ZZERO) { // cons
|
|
markobject(arg);
|
|
obj = cdr(obj);
|
|
|
|
goto MARK;
|
|
}
|
|
|
|
if (type == ARRAY) {
|
|
obj = cdr(obj);
|
|
|
|
goto MARK;
|
|
}
|
|
|
|
if ((type == STRING) || (type == SYMBOL && longsymbolp(obj))) {
|
|
obj = cdr(obj);
|
|
|
|
while (obj != NULL) {
|
|
arg = car(obj);
|
|
mark(obj);
|
|
obj = arg;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
sweep - goes through the workspace freeing objects that have not
|
|
been marked, and unmarks marked objects
|
|
*/
|
|
void
|
|
sweep()
|
|
{
|
|
Freelist = NULL;
|
|
Freespace = 0;
|
|
|
|
for (int i=WORKSPACESIZE-1; i>=0; i--) {
|
|
object *obj = &Workspace[i];
|
|
if (!marked(obj)) {
|
|
myfree(obj);
|
|
} else {
|
|
unmark(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
gc - performs garbage collection by calling markobject() on each
|
|
of the pointers to objects in use, followed by sweep() to free
|
|
unused objects.
|
|
*/
|
|
void
|
|
gc(object *form, object *env)
|
|
{
|
|
#if defined(printgcs)
|
|
int start = Freespace;
|
|
#endif
|
|
|
|
markobject(tee);
|
|
markobject(GlobalEnv);
|
|
markobject(GCStack);
|
|
markobject(form);
|
|
markobject(env);
|
|
sweep();
|
|
|
|
#if defined(printgcs)
|
|
pfl(pserial);
|
|
pserial('{');
|
|
pint(Freespace - start, pserial);
|
|
pserial('}');
|
|
#endif
|
|
}
|
|
|
|
// Compact image
|
|
|
|
/*
|
|
movepointer - Corrects pointers to an object that has been moved
|
|
from 'from' to 'to'. Only need to scan addresses below 'from'
|
|
as there are no accessible objects above that.
|
|
*/
|
|
void
|
|
movepointer(object *from, object *to)
|
|
{
|
|
uintptr_t limit = ((uintptr_t)(from) - (uintptr_t)(Workspace))/sizeof(uintptr_t);
|
|
for (uintptr_t i=0; i<limit; i++) {
|
|
object *obj = &Workspace[i];
|
|
unsigned int type = (obj->type) & ~MARKBIT;
|
|
if (marked(obj) && (type >= ARRAY || type==ZZERO || \
|
|
(type == SYMBOL && longsymbolp(obj)))) {
|
|
if (car(obj) == (object *)((uintptr_t)from | MARKBIT)) {
|
|
car(obj) = (object *)((uintptr_t)to | MARKBIT);
|
|
}
|
|
|
|
if (cdr(obj) == from) {
|
|
cdr(obj) = to;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fix strings and long symbols for (uintptr_t i=0; i<limit; i++)
|
|
for (uintptr_t i=0; i<limit; i++) {
|
|
object *obj = &Workspace[i];
|
|
if (marked(obj)) {
|
|
unsigned int type = (obj->type) & ~MARKBIT;
|
|
if (type == STRING || (type == SYMBOL && longsymbolp(obj))) {
|
|
obj = cdr(obj);
|
|
while (obj != NULL) {
|
|
if (cdr(obj) == to) {
|
|
cdr(obj) = from;
|
|
}
|
|
|
|
obj = (object *)((uintptr_t)(car(obj)) & ~MARKBIT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
compactimage - Marks all accessible objects. Moves the last marked
|
|
object down to the first free space gap, correcting pointers by
|
|
calling movepointer(). Then repeats until there are no more gaps.
|
|
*/
|
|
uintptr_t
|
|
compactimage(object **arg)
|
|
{
|
|
markobject(tee);
|
|
markobject(GlobalEnv);
|
|
markobject(GCStack);
|
|
object *firstfree = Workspace;
|
|
|
|
while (marked(firstfree)) {
|
|
firstfree++;
|
|
}
|
|
|
|
object *obj = &Workspace[WORKSPACESIZE-1];
|
|
|
|
while (firstfree < obj) {
|
|
if (marked(obj)) {
|
|
car(firstfree) = car(obj);
|
|
cdr(firstfree) = cdr(obj);
|
|
unmark(obj);
|
|
movepointer(obj, firstfree);
|
|
|
|
if (GlobalEnv == obj) {
|
|
GlobalEnv = firstfree;
|
|
}
|
|
|
|
if (GCStack == obj) {
|
|
GCStack = firstfree;
|
|
}
|
|
|
|
if (*arg == obj) {
|
|
*arg = firstfree;
|
|
}
|
|
|
|
while (marked(firstfree)) {
|
|
firstfree++;
|
|
}
|
|
}
|
|
obj--;
|
|
}
|
|
|
|
sweep();
|
|
return firstfree - Workspace;
|
|
}
|
|
|
|
// Make SD card filename
|
|
|
|
char *
|
|
MakeFilename(object *arg, char *buffer)
|
|
{
|
|
int max = BUFFERSIZE-1;
|
|
buffer[0]='/';
|
|
int i = 1;
|
|
|
|
do {
|
|
char c = nthchar(arg, i-1);
|
|
if (c == '\0') {
|
|
break;
|
|
}
|
|
|
|
buffer[i++] = c;
|
|
} while (i<max);
|
|
|
|
buffer[i] = '\0';
|
|
return buffer;
|
|
}
|
|
|
|
// Save-image and load-image
|
|
|
|
#if defined(sdcardsupport)
|
|
|
|
/*
|
|
SDBegin - a standard call on all platforms to initialise the SD Card interface.
|
|
*/
|
|
void
|
|
SDBegin()
|
|
{
|
|
#if defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER)
|
|
SD.begin(SDCARD_SS_PIN, SPI1);
|
|
#else
|
|
SD.begin(SDCARD_SS_PIN);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
SDWrite32(File file, int data)
|
|
{
|
|
file.write(data & 0xFF);
|
|
file.write(data>>8 & 0xFF);
|
|
file.write(data>>16 & 0xFF);
|
|
file.write(data>>24 & 0xFF);
|
|
}
|
|
|
|
int
|
|
SDRead32(File file)
|
|
{
|
|
uintptr_t b0 = file.read();
|
|
uintptr_t b1 = file.read();
|
|
uintptr_t b2 = file.read();
|
|
uintptr_t b3 = file.read();
|
|
|
|
return b0 | b1<<8 | b2<<16 | b3<<24;
|
|
}
|
|
|
|
#elif defined(LITTLEFS)
|
|
void
|
|
FSWrite32(File file, uint32_t data)
|
|
{
|
|
union {
|
|
uint32_t data2;
|
|
uint8_t u8[4];
|
|
};
|
|
|
|
data2 = data;
|
|
if (file.write(u8, 4) != 4) {
|
|
error2("not enough room");
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
FSRead32(File file)
|
|
{
|
|
union {
|
|
uint32_t data;
|
|
uint8_t u8[4];
|
|
};
|
|
|
|
file.read(u8, 4);
|
|
return data;
|
|
}
|
|
|
|
#elif defined(DATAFLASH)
|
|
// Winbond DataFlash support for Adafruit M4 Express boards
|
|
#define PAGEPROG 0x02
|
|
#define READSTATUS 0x05
|
|
#define READDATA 0x03
|
|
#define WRITEENABLE 0x06
|
|
#define BLOCK64K 0xD8
|
|
#define READID 0x90
|
|
|
|
// Arduino pins used for dataflash
|
|
#if defined(ARDUINO_ITSYBITSY_M0) || defined(ARDUINO_SAMD_FEATHER_M0_EXPRESS)
|
|
const int sck = 38, ssel = 39, mosi = 37, miso = 36;
|
|
#elif defined(EXTERNAL_FLASH_USE_QSPI)
|
|
const int sck = PIN_QSPI_SCK, ssel = PIN_QSPI_CS, mosi = PIN_QSPI_IO0, miso = PIN_QSPI_IO1;
|
|
#endif
|
|
|
|
void
|
|
FlashBusy()
|
|
{
|
|
digitalWrite(ssel, 0);
|
|
FlashWrite(READSTATUS);
|
|
|
|
while ((FlashReadByte() & 1) != 0);
|
|
|
|
digitalWrite(ssel, 1);
|
|
}
|
|
|
|
inline void
|
|
FlashWrite(uint8_t data)
|
|
{
|
|
shiftOut(mosi, sck, MSBFIRST, data);
|
|
}
|
|
|
|
inline uint8_t
|
|
FlashReadByte()
|
|
{
|
|
return shiftIn(miso, sck, MSBFIRST);
|
|
}
|
|
|
|
void
|
|
FlashWriteByte(uint32_t *addr, uint8_t data)
|
|
{
|
|
// New page
|
|
if (((*addr) & 0xFF) == 0) {
|
|
digitalWrite(ssel, 1);
|
|
FlashBusy();
|
|
FlashWriteEnable();
|
|
digitalWrite(ssel, 0);
|
|
FlashWrite(PAGEPROG);
|
|
FlashWrite((*addr)>>16);
|
|
FlashWrite((*addr)>>8);
|
|
FlashWrite(0);
|
|
}
|
|
|
|
FlashWrite(data);
|
|
(*addr)++;
|
|
}
|
|
|
|
void
|
|
FlashWriteEnable()
|
|
{
|
|
digitalWrite(ssel, 0);
|
|
FlashWrite(WRITEENABLE);
|
|
digitalWrite(ssel, 1);
|
|
}
|
|
|
|
bool
|
|
FlashCheck()
|
|
{
|
|
uint8_t devID;
|
|
|
|
digitalWrite(ssel, HIGH); pinMode(ssel, OUTPUT);
|
|
pinMode(sck, OUTPUT);
|
|
pinMode(mosi, OUTPUT);
|
|
pinMode(miso, INPUT);
|
|
digitalWrite(sck, LOW); digitalWrite(mosi, HIGH);
|
|
digitalWrite(ssel, LOW);
|
|
FlashWrite(READID);
|
|
|
|
for (uint8_t i=0; i<4; i++) {
|
|
FlashReadByte();
|
|
}
|
|
|
|
devID = FlashReadByte();
|
|
digitalWrite(ssel, HIGH);
|
|
|
|
return (devID >= 0x14 && devID <= 0x17); // true = found correct device
|
|
}
|
|
|
|
void
|
|
FlashBeginWrite(uint32_t *addr, uint32_t bytes)
|
|
{
|
|
*addr = 0;
|
|
uint8_t blocks = (bytes+65535)/65536;
|
|
|
|
// Erase 64K
|
|
for (uint8_t b=0; b<blocks; b++) {
|
|
FlashWriteEnable();
|
|
digitalWrite(ssel, 0);
|
|
FlashWrite(BLOCK64K);
|
|
FlashWrite(b);
|
|
FlashWrite(0);
|
|
FlashWrite(0);
|
|
digitalWrite(ssel, 1);
|
|
FlashBusy();
|
|
}
|
|
}
|
|
|
|
void
|
|
FlashWrite32(uint32_t *addr, uint32_t data)
|
|
{
|
|
FlashWriteByte(addr, data & 0xFF);
|
|
FlashWriteByte(addr, data>>8 & 0xFF);
|
|
FlashWriteByte(addr, data>>16 & 0xFF);
|
|
FlashWriteByte(addr, data>>24 & 0xFF);
|
|
}
|
|
|
|
inline void
|
|
FlashEndWrite (uint32_t *addr)
|
|
{
|
|
(void) addr;
|
|
digitalWrite(ssel, 1);
|
|
FlashBusy();
|
|
}
|
|
|
|
void
|
|
FlashBeginRead(uint32_t *addr)
|
|
{
|
|
*addr = 0;
|
|
FlashBusy();
|
|
digitalWrite(ssel, 0);
|
|
FlashWrite(READDATA);
|
|
FlashWrite(0);
|
|
FlashWrite(0);
|
|
FlashWrite(0);
|
|
}
|
|
|
|
uint32_t
|
|
FlashRead32(uint32_t *addr)
|
|
{
|
|
(void) addr;
|
|
uint8_t b0 = FlashReadByte();
|
|
uint8_t b1 = FlashReadByte();
|
|
uint8_t b2 = FlashReadByte();
|
|
uint8_t b3 = FlashReadByte();
|
|
|
|
return b0 | b1<<8 | b2<<16 | b3<<24;
|
|
}
|
|
|
|
inline void
|
|
FlashEndRead(uint32_t *addr)
|
|
{
|
|
(void) addr;
|
|
digitalWrite(ssel, 1);
|
|
}
|
|
|
|
#elif defined(CPUFLASH)
|
|
// For ATSAMD21
|
|
__attribute__((__aligned__(256))) static const uint8_t flash_store[FLASHSIZE] = { };
|
|
|
|
void
|
|
row_erase(const volatile void *addr)
|
|
{
|
|
NVMCTRL->ADDR.reg = ((uint32_t)addr) / 2;
|
|
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
|
|
|
|
while (!NVMCTRL->INTFLAG.bit.READY);
|
|
}
|
|
|
|
void
|
|
page_clear()
|
|
{
|
|
// Execute "PBC" Page Buffer Clear
|
|
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
|
|
while (NVMCTRL->INTFLAG.bit.READY == 0);
|
|
}
|
|
|
|
void
|
|
page_write()
|
|
{
|
|
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
|
|
while (NVMCTRL->INTFLAG.bit.READY == 0);
|
|
}
|
|
|
|
bool
|
|
FlashCheck()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void
|
|
FlashBeginWrite(uint32_t *addr, uint32_t bytes)
|
|
{
|
|
(void) bytes;
|
|
*addr = (uint32_t)flash_store;
|
|
|
|
// Disable automatic page write
|
|
NVMCTRL->CTRLB.bit.MANW = 1;
|
|
}
|
|
|
|
void
|
|
FlashWrite32(uint32_t *addr, uint32_t data)
|
|
{
|
|
if (((*addr) & 0xFF) == 0) {
|
|
row_erase((const volatile void *)(*addr));
|
|
}
|
|
|
|
if (((*addr) & 0x3F) == 0) {
|
|
page_clear();
|
|
}
|
|
|
|
*(volatile uint32_t *)(*addr) = data;
|
|
(*addr) = (*addr) + 4;
|
|
|
|
if (((*addr) & 0x3F) == 0) {
|
|
page_write();
|
|
}
|
|
}
|
|
|
|
void
|
|
FlashEndWrite(uint32_t *addr)
|
|
{
|
|
if (((*addr) & 0x3F) != 0) {
|
|
page_write();
|
|
}
|
|
}
|
|
|
|
void
|
|
FlashBeginRead(uint32_t *addr)
|
|
{
|
|
*addr = (uint32_t)flash_store;
|
|
}
|
|
|
|
uint32_t
|
|
FlashRead32(uint32_t *addr)
|
|
{
|
|
uint32_t data = *(volatile const uint32_t *)(*addr);
|
|
(*addr) = (*addr) + 4;
|
|
|
|
return data;
|
|
}
|
|
|
|
void
|
|
FlashEndRead(uint32_t *addr)
|
|
{
|
|
(void) addr;
|
|
}
|
|
|
|
#elif defined(EEPROMFLASH)
|
|
bool
|
|
FlashCheck()
|
|
{
|
|
return (EEPROM.length() == FLASHSIZE);
|
|
}
|
|
|
|
void
|
|
FlashBeginWrite(uint32_t *addr, uint32_t bytes)
|
|
{
|
|
(void) bytes;
|
|
*addr = 0;
|
|
}
|
|
|
|
void
|
|
FlashWrite32(uint32_t *addr, uint32_t data)
|
|
{
|
|
EEPROM.put(*addr, data);
|
|
(*addr) = (*addr) + 4;
|
|
}
|
|
|
|
void
|
|
FlashEndWrite(uint32_t *addr)
|
|
{
|
|
(void) addr;
|
|
}
|
|
|
|
void
|
|
FlashBeginRead(uint32_t *addr)
|
|
{
|
|
*addr = 0;
|
|
}
|
|
|
|
uint32_t
|
|
FlashRead32(uint32_t *addr)
|
|
{
|
|
uint32_t data;
|
|
EEPROM.get(*addr, data);
|
|
(*addr) = (*addr) + 4;
|
|
|
|
return data;
|
|
}
|
|
|
|
void
|
|
FlashEndRead(uint32_t *addr)
|
|
{
|
|
(void) addr;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
saveimage - saves an image of the workspace to the persistent
|
|
storage selected for the platform.
|
|
*/
|
|
int
|
|
saveimage(object *arg)
|
|
{
|
|
#if defined(sdcardsupport)
|
|
unsigned int imagesize = compactimage(&arg);
|
|
#if defined(RASPBERRY_PI_PICO_PLATFORM)
|
|
#if defined(PLATFORM_PICOCALC)
|
|
if(!SD.begin(SDCARD_SS_PIN, (uint32_t) SPI_HALF_SPEED, SPI)){
|
|
error2(PSTR("failed to init SD card"));
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
SD.begin(SDCARD_SS_PIN,(uint32_t) SPI_HALF_SPEED, SPI1);
|
|
#endif
|
|
#else
|
|
SDBegin();
|
|
#endif
|
|
|
|
File file;
|
|
|
|
if (stringp(arg)) {
|
|
char buffer[BUFFERSIZE];
|
|
|
|
file = SD.open(MakeFilename(arg, buffer), O_RDWR | O_CREAT | O_TRUNC);
|
|
if (!file) {
|
|
error2("problem saving to SD card or invalid filename");
|
|
}
|
|
|
|
arg = NULL;
|
|
} else if (arg == NULL || listp(arg)) {
|
|
file = SD.open("/ULISP.IMG", O_RDWR | O_CREAT | O_TRUNC);
|
|
if (!file) {
|
|
error2("problem saving to SD card");
|
|
}
|
|
} else {
|
|
error(invalidarg, arg);
|
|
}
|
|
|
|
SDWrite32(file, (uintptr_t)arg);
|
|
SDWrite32(file, imagesize);
|
|
SDWrite32(file, (uintptr_t)GlobalEnv);
|
|
SDWrite32(file, (uintptr_t)GCStack);
|
|
|
|
for (int i=0; i<CODESIZE; i++) {
|
|
file.write(MyCode[i]);
|
|
}
|
|
|
|
for (unsigned int i=0; i<imagesize; i++) {
|
|
object *obj = &Workspace[i];
|
|
SDWrite32(file, (uintptr_t)car(obj));
|
|
SDWrite32(file, (uintptr_t)cdr(obj));
|
|
}
|
|
file.close();
|
|
|
|
return imagesize;
|
|
#elif defined(LITTLEFS) && !defined(sdcardsupport)
|
|
unsigned int imagesize = compactimage(&arg);
|
|
LittleFS.begin(LITTLEFS);
|
|
File file;
|
|
|
|
if (stringp(arg)) {
|
|
char buffer[BUFFERSIZE];
|
|
|
|
file = LittleFS.open(MakeFilename(arg, buffer), FS_FILE_WRITE);
|
|
if (!file) {
|
|
error2("problem saving to LittleFS or invalid filename");
|
|
}
|
|
|
|
arg = NULL;
|
|
} else if (arg == NULL || listp(arg)) {
|
|
file = LittleFS.open("/ULISP.IMG", FS_FILE_WRITE);
|
|
if (!file) {
|
|
error2("problem saving to LittleFS");
|
|
}
|
|
} else {
|
|
error(invalidarg, arg);
|
|
}
|
|
|
|
FSWrite32(file, (uintptr_t)arg);
|
|
FSWrite32(file, imagesize);
|
|
FSWrite32(file, (uintptr_t)GlobalEnv);
|
|
FSWrite32(file, (uintptr_t)GCStack);
|
|
|
|
if (file.write(MyCode, CODESIZE) != CODESIZE) {
|
|
error2("not enough room");
|
|
}
|
|
|
|
for (unsigned int i=0; i<imagesize; i++) {
|
|
object *obj = &Workspace[i];
|
|
FSWrite32(file, (uintptr_t)car(obj));
|
|
FSWrite32(file, (uintptr_t)cdr(obj));
|
|
}
|
|
file.close();
|
|
|
|
return imagesize;
|
|
#elif defined(DATAFLASH) || defined(CPUFLASH) || defined(EEPROMFLASH)
|
|
unsigned int imagesize = compactimage(&arg);
|
|
|
|
if (!(arg == NULL || listp(arg))) {
|
|
error(invalidarg, arg);
|
|
}
|
|
|
|
if (!FlashCheck()) {
|
|
error2("flash not available");
|
|
}
|
|
|
|
// Save to flash
|
|
uint32_t bytesneeded = 16 + CODESIZE + imagesize*8;
|
|
|
|
if (bytesneeded > FLASHSIZE) {
|
|
error("image too large", number(imagesize));
|
|
}
|
|
|
|
uint32_t addr;
|
|
FlashBeginWrite(&addr, bytesneeded);
|
|
FlashWrite32(&addr, (uintptr_t)arg);
|
|
FlashWrite32(&addr, imagesize);
|
|
FlashWrite32(&addr, (uintptr_t)GlobalEnv);
|
|
FlashWrite32(&addr, (uintptr_t)GCStack);
|
|
|
|
for (int i=0; i<CODESIZE; i=i+4) {
|
|
union {
|
|
uint32_t u32;
|
|
uint8_t u8[4];
|
|
};
|
|
|
|
u8[0] = MyCode[i];
|
|
u8[1] = MyCode[i+1];
|
|
u8[2] = MyCode[i+2];
|
|
u8[3] = MyCode[i+3];
|
|
FlashWrite32(&addr, u32);
|
|
}
|
|
|
|
for (unsigned int i=0; i<imagesize; i++) {
|
|
object *obj = &Workspace[i];
|
|
FlashWrite32(&addr, (uintptr_t)car(obj));
|
|
FlashWrite32(&addr, (uintptr_t)cdr(obj));
|
|
}
|
|
|
|
FlashEndWrite(&addr);
|
|
|
|
return imagesize;
|
|
#else
|
|
(void) arg;
|
|
error2("not available");
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
loadimage - loads an image of the workspace from the persistent
|
|
storage selected for the platform.
|
|
*/
|
|
int
|
|
loadimage(object *arg)
|
|
{
|
|
#if defined(sdcardsupport)
|
|
#if defined(RASPBERRY_PI_PICO_PLATFORM)
|
|
#if defined(PLATFORM_PICOCALC)
|
|
if(!SD.begin(SDCARD_SS_PIN,(uint32_t) SPI_HALF_SPEED, SPI)){
|
|
error2(PSTR("failed to init SD card"));
|
|
return 0;
|
|
}
|
|
#else
|
|
SD.begin(SDCARD_SS_PIN,(uint32_t) SPI_HALF_SPEED, SPI1);
|
|
#endif
|
|
#else
|
|
SD.begin(SDCARD_SS_PIN);
|
|
#endif
|
|
|
|
File file;
|
|
if (stringp(arg)) {
|
|
char buffer[BUFFERSIZE];
|
|
|
|
file = SD.open(MakeFilename(arg, buffer));
|
|
if (!file) {
|
|
error2("problem loading from SD card or invalid filename");
|
|
}
|
|
} else if (arg == NULL) {
|
|
if (!SD.exists("/ULISP.IMG")) {
|
|
error2("workspace image not found");
|
|
}
|
|
|
|
file = SD.open("/ULISP.IMG");
|
|
if (!file) {
|
|
error2("problem loading from SD card");
|
|
}
|
|
} else {
|
|
error(invalidarg, arg);
|
|
}
|
|
|
|
SDRead32(file);
|
|
unsigned int imagesize = SDRead32(file);
|
|
GlobalEnv = (object *)SDRead32(file);
|
|
GCStack = (object *)SDRead32(file);
|
|
|
|
for (int i=0; i<CODESIZE; i++) {
|
|
MyCode[i] = file.read();
|
|
}
|
|
|
|
for (unsigned int i=0; i<imagesize; i++) {
|
|
object *obj = &Workspace[i];
|
|
car(obj) = (object *)SDRead32(file);
|
|
cdr(obj) = (object *)SDRead32(file);
|
|
}
|
|
|
|
file.close();
|
|
gc(NULL, NULL);
|
|
|
|
pfstring(PSTR("image loaded"), pserial);
|
|
pln(pserial);
|
|
return imagesize;
|
|
|
|
#elif defined(LITTLEFS)
|
|
LittleFS.begin(LITTLEFS);
|
|
File file;
|
|
if (stringp(arg)) {
|
|
char buffer[BUFFERSIZE];
|
|
file = LittleFS.open(MakeFilename(arg, buffer), FS_FILE_READ);
|
|
if (!file) {
|
|
error2("problem loading from LittleFS or invalid filename");
|
|
}
|
|
} else if (arg == NULL) {
|
|
file = LittleFS.open("/ULISP.IMG", FS_FILE_READ);
|
|
if (!file) {
|
|
error2("problem loading from LittleFS");
|
|
}
|
|
} else {
|
|
error(invalidarg, arg);
|
|
}
|
|
|
|
FSRead32(file);
|
|
unsigned int imagesize = FSRead32(file);
|
|
GlobalEnv = (object *)FSRead32(file);
|
|
GCStack = (object *)FSRead32(file);
|
|
file.read(MyCode, CODESIZE);
|
|
|
|
for (unsigned int i=0; i<imagesize; i++) {
|
|
object *obj = &Workspace[i];
|
|
car(obj) = (object *)FSRead32(file);
|
|
cdr(obj) = (object *)FSRead32(file);
|
|
}
|
|
|
|
file.close();
|
|
gc(NULL, NULL);
|
|
return imagesize;
|
|
|
|
#elif defined(DATAFLASH) || defined(CPUFLASH) || defined(EEPROMFLASH)
|
|
(void) arg;
|
|
if (!FlashCheck()) {
|
|
error2("flash not available");
|
|
}
|
|
|
|
uint32_t addr;
|
|
FlashBeginRead(&addr);
|
|
FlashRead32(&addr); // Skip eval address
|
|
uint32_t imagesize = FlashRead32(&addr);
|
|
if (imagesize == 0 || imagesize == 0xFFFFFFFF) {
|
|
error2("no saved image");
|
|
}
|
|
|
|
GlobalEnv = (object *)FlashRead32(&addr);
|
|
GCStack = (object *)FlashRead32(&addr);
|
|
for (int i=0; i<CODESIZE; i=i+4) {
|
|
union {
|
|
uint32_t u32;
|
|
uint8_t u8[4];
|
|
};
|
|
|
|
u32 = FlashRead32(&addr);
|
|
|
|
MyCode[i] = u8[0];
|
|
MyCode[i+1] = u8[1];
|
|
MyCode[i+2] = u8[2];
|
|
MyCode[i+3] = u8[3];
|
|
}
|
|
|
|
for (uint32_t i=0; i<imagesize; i++) {
|
|
object *obj = &Workspace[i];
|
|
car(obj) = (object *)FlashRead32(&addr);
|
|
cdr(obj) = (object *)FlashRead32(&addr);
|
|
}
|
|
|
|
FlashEndRead(&addr);
|
|
gc(NULL, NULL);
|
|
return imagesize;
|
|
|
|
#else
|
|
(void) arg;
|
|
error2("not available");
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
autorunimage - loads and runs an image of the workspace from the
|
|
persistent storage selected for the platform.
|
|
*/
|
|
void
|
|
autorunimage()
|
|
{
|
|
#if defined(sdcardsupport)
|
|
#if defined(RASPBERRY_PI_PICO_PLATFORM)
|
|
#if defined(PLATFORM_PICOCALC)
|
|
if(!SD.begin(SDCARD_SS_PIN, (uint32_t) SPI_HALF_SPEED, SPI)){
|
|
error2(PSTR("failed to init SD card"));
|
|
return;
|
|
}
|
|
#else
|
|
SD.begin(SDCARD_SS_PIN,(uint32_t) SPI_HALF_SPEED, SPI1);
|
|
#endif
|
|
#else
|
|
SD.begin(SDCARD_SS_PIN);
|
|
#endif
|
|
SDBegin();
|
|
File file = SD.open("/ULISP.IMG");
|
|
|
|
if (!file) {
|
|
error2("unable to autorun from SD card");
|
|
}
|
|
|
|
object *autorun = (object *)SDRead32(file);
|
|
file.close();
|
|
if (autorun != NULL) {
|
|
loadimage(NULL);
|
|
apply(autorun, NULL, NULL);
|
|
}
|
|
|
|
#elif defined(LITTLEFS)
|
|
LittleFS.begin(LITTLEFS);
|
|
File file = LittleFS.open("/ULISP.IMG", FS_FILE_READ);
|
|
|
|
if (!file) {
|
|
error2("problem autorunning from LittleFS");
|
|
}
|
|
|
|
object *autorun = (object *)FSRead32(file);
|
|
file.close();
|
|
|
|
if (autorun != NULL) {
|
|
loadimage(NULL);
|
|
apply(autorun, NULL, NULL);
|
|
}
|
|
|
|
#elif defined(DATAFLASH) || defined(CPUFLASH) || defined(EEPROMFLASH)
|
|
if (!FlashCheck()) {
|
|
error2("flash not available");
|
|
}
|
|
|
|
uint32_t addr;
|
|
FlashBeginRead(&addr);
|
|
object *autorun = (object *)FlashRead32(&addr);
|
|
FlashEndRead(&addr);
|
|
if (autorun != NULL && (unsigned int)autorun != 0xFFFFFFFF) {
|
|
loadimage(nil);
|
|
apply(autorun, NULL, NULL);
|
|
}
|
|
#else
|
|
error2("autorun not available");
|
|
#endif
|
|
}
|
|
|
|
// Tracing
|
|
|
|
/*
|
|
tracing - returns a number between 1 and TRACEMAX if name is being traced, or 0 otherwise
|
|
*/
|
|
int
|
|
tracing(symbol_t name)
|
|
{
|
|
int i = 0;
|
|
|
|
while (i < TRACEMAX) {
|
|
if (TraceFn[i] == name) {
|
|
return i+1;
|
|
}
|
|
i++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
trace - enables tracing of symbol name and adds it to the array TraceFn[].
|
|
*/
|
|
void
|
|
trace(symbol_t name)
|
|
{
|
|
if (tracing(name)) {
|
|
error("already being traced", symbol(name));
|
|
}
|
|
|
|
int i = 0;
|
|
while (i < TRACEMAX) {
|
|
if (TraceFn[i] == 0) {
|
|
TraceFn[i] = name;
|
|
TraceDepth[i] = 0;
|
|
return;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
error2("already tracing " stringify(TRACEMAX) " functions");
|
|
}
|
|
|
|
/*
|
|
untrace - disables tracing of symbol name and removes it from the array TraceFn[].
|
|
*/
|
|
void
|
|
untrace(symbol_t name)
|
|
{
|
|
int i = 0;
|
|
|
|
while (i < TRACEMAX) {
|
|
if (TraceFn[i] == name) {
|
|
TraceFn[i] = 0;
|
|
return;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
error("not tracing", symbol(name));
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
/*
|
|
consp - implements Lisp consp
|
|
*/
|
|
bool
|
|
consp(object *x)
|
|
{
|
|
if (x == NULL) {
|
|
return false;
|
|
}
|
|
|
|
unsigned int type = x->type;
|
|
return type >= PAIR || type == ZZERO;
|
|
}
|
|
|
|
/*
|
|
atom - implements Lisp atom
|
|
*/
|
|
#define atom(x) (!consp(x))
|
|
|
|
/*
|
|
listp - implements Lisp listp
|
|
*/
|
|
bool
|
|
listp(object *x)
|
|
{
|
|
if (x == NULL) {
|
|
return true;
|
|
}
|
|
|
|
unsigned int type = x->type;
|
|
return type >= PAIR || type == ZZERO;
|
|
}
|
|
|
|
/*
|
|
improperp - tests whether x is an improper list
|
|
*/
|
|
#define improperp(x) (!listp(x))
|
|
|
|
object *
|
|
quote(object *arg)
|
|
{
|
|
return cons(bsymbol(QUOTE), cons(arg,NULL));
|
|
}
|
|
|
|
// Radix 40 encoding
|
|
|
|
/*
|
|
builtin - converts a symbol name to builtin
|
|
*/
|
|
builtin_t
|
|
builtin(symbol_t name)
|
|
{
|
|
return (builtin_t)(untwist(name) - BUILTINS);
|
|
}
|
|
|
|
/*
|
|
sym - converts a builtin to a symbol name
|
|
*/
|
|
symbol_t
|
|
sym (builtin_t x)
|
|
{
|
|
return twist(x + BUILTINS);
|
|
}
|
|
|
|
/*
|
|
toradix40 - returns a number from 0 to 39 if the character can be encoded, or -1 otherwise.
|
|
*/
|
|
int8_t
|
|
toradix40(char ch)
|
|
{
|
|
if (ch == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (ch >= '0' && ch <= '9') {
|
|
return ch-'0'+1;
|
|
}
|
|
|
|
if (ch == '-') {
|
|
return 37;
|
|
}
|
|
|
|
if (ch == '*') {
|
|
return 38;
|
|
}
|
|
|
|
if (ch == '$') {
|
|
return 39;
|
|
}
|
|
|
|
ch = ch | 0x20;
|
|
if (ch >= 'a' && ch <= 'z') {
|
|
return ch-'a'+11;
|
|
}
|
|
|
|
return -1; // Invalid
|
|
}
|
|
|
|
/*
|
|
fromradix40 - returns the character encoded by the number n.
|
|
*/
|
|
char
|
|
fromradix40(char n)
|
|
{
|
|
if (n >= 1 && n <= 10) {
|
|
return '0'+n-1;
|
|
}
|
|
|
|
if (n >= 11 && n <= 36) {
|
|
return 'a'+n-11;
|
|
}
|
|
|
|
if (n == 37) {
|
|
return '-';
|
|
}
|
|
|
|
if (n == 38) {
|
|
return '*';
|
|
}
|
|
|
|
if (n == 39) {
|
|
return '$';
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
pack40 - packs six radix40-encoded characters from buffer into a
|
|
32-bit number and returns it.
|
|
*/
|
|
uint32_t
|
|
pack40(char *buffer)
|
|
{
|
|
int x = 0, j = 0;
|
|
for (int i=0; i<6; i++) {
|
|
x = x * 40 + toradix40(buffer[j]);
|
|
if (buffer[j] != 0) {
|
|
j++;
|
|
}
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
/*
|
|
valid40 - returns true if the symbol in buffer can be encoded as
|
|
six radix40-encoded characters.
|
|
*/
|
|
bool
|
|
valid40(char *buffer)
|
|
{
|
|
int t = 11;
|
|
|
|
for (int i=0; i<6; i++) {
|
|
if (toradix40(buffer[i]) < t) {
|
|
return false;
|
|
}
|
|
|
|
if (buffer[i] == 0) {
|
|
break;
|
|
}
|
|
|
|
t = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
digitvalue - returns the numerical value of a hexadecimal digit,
|
|
or 16 if invalid.
|
|
*/
|
|
int8_t
|
|
digitvalue(char d)
|
|
{
|
|
if (d>='0' && d<='9') {
|
|
return d-'0';
|
|
}
|
|
|
|
d = d | 0x20;
|
|
if (d>='a' && d<='f') {
|
|
return d-'a'+10;
|
|
}
|
|
|
|
return 16;
|
|
}
|
|
|
|
/*
|
|
checkinteger - check that obj is an integer and return it
|
|
*/
|
|
int
|
|
checkinteger(object *obj)
|
|
{
|
|
if (!integerp(obj)) {
|
|
error(notaninteger, obj);
|
|
}
|
|
|
|
return obj->integer;
|
|
}
|
|
|
|
/*
|
|
checkbitvalue - check that obj is an integer equal to 0 or 1 and
|
|
return it
|
|
*/
|
|
int
|
|
checkbitvalue(object *obj)
|
|
{
|
|
if (!integerp(obj)) {
|
|
error(notaninteger, obj);
|
|
}
|
|
|
|
int n = obj->integer;
|
|
if (n & ~1) {
|
|
error("argument is not a bit value", obj);
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
checkintfloat - check that obj is an integer or floating-point
|
|
number and return the number
|
|
*/
|
|
float
|
|
checkintfloat(object *obj)
|
|
{
|
|
if (integerp(obj)) {
|
|
return (float)obj->integer;
|
|
}
|
|
|
|
if (!floatp(obj)) {
|
|
error(notanumber, obj);
|
|
}
|
|
|
|
return obj->single_float;
|
|
}
|
|
|
|
/*
|
|
checkchar - check that obj is a character and return the character
|
|
*/
|
|
int
|
|
checkchar(object *obj)
|
|
{
|
|
if (!characterp(obj)) {
|
|
error("argument is not a character", obj);
|
|
}
|
|
|
|
return obj->chars;
|
|
}
|
|
|
|
/*
|
|
checkstring - check that obj is a string
|
|
*/
|
|
object *
|
|
checkstring(object *obj)
|
|
{
|
|
if (!stringp(obj)) {
|
|
error(notastring, obj);
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
int
|
|
isstream(object *obj)
|
|
{
|
|
if (!streamp(obj)) {
|
|
error("not a stream", obj);
|
|
}
|
|
|
|
return obj->integer;
|
|
}
|
|
|
|
int
|
|
isbuiltin(object *obj, builtin_t n)
|
|
{
|
|
return symbolp(obj) && obj->name == sym(n);
|
|
}
|
|
|
|
bool
|
|
builtinp(symbol_t name)
|
|
{
|
|
return (untwist(name) >= BUILTINS);
|
|
}
|
|
|
|
int
|
|
checkkeyword(object *obj)
|
|
{
|
|
if (!keywordp(obj)) {
|
|
error("argument is not a keyword", obj);
|
|
}
|
|
|
|
builtin_t kname = builtin(obj->name);
|
|
uint8_t context = getminmax(kname);
|
|
if (context != 0 && context != Context) {
|
|
error(invalidkey, obj);
|
|
}
|
|
|
|
return ((int)lookupfn(kname));
|
|
}
|
|
|
|
/*
|
|
checkargs - checks that the number of objects in the list args
|
|
is within the range specified in the symbol lookup table
|
|
*/
|
|
void
|
|
checkargs(object *args)
|
|
{
|
|
int nargs = listlength(args);
|
|
|
|
checkminmax(Context, nargs);
|
|
}
|
|
|
|
/*
|
|
eqlongsymbol - checks whether two long symbols are equal
|
|
*/
|
|
bool
|
|
eqlongsymbol(symbol_t sym1, symbol_t sym2)
|
|
{
|
|
object *arg1 = (object *)sym1; object *arg2 = (object *)sym2;
|
|
|
|
while ((arg1 != NULL) || (arg2 != NULL)) {
|
|
if (arg1 == NULL || arg2 == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (arg1->chars != arg2->chars) {
|
|
return false;
|
|
}
|
|
|
|
arg1 = car(arg1); arg2 = car(arg2);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
eqsymbol - checks whether two symbols are equal
|
|
*/
|
|
bool eqsymbol (symbol_t sym1, symbol_t sym2) {
|
|
if (!longnamep(sym1) && !longnamep(sym2)) return (sym1 == sym2); // Same short symbol
|
|
if (longnamep(sym1) && longnamep(sym2)) return eqlongsymbol(sym1, sym2); // Same long symbol
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
eq - implements Lisp eq, taking into account PSRAM
|
|
*/
|
|
bool eq (object *arg1, object *arg2) {
|
|
if (arg1 == arg2) return true; // Same object
|
|
if ((arg1 == nil) || (arg2 == nil)) return false; // Not both values
|
|
#if !defined(BOARD_HAS_PSRAM)
|
|
if (arg1->cdr != arg2->cdr) return false; // Different values
|
|
if (symbolp(arg1) && symbolp(arg2)) return true; // Same symbol
|
|
#else
|
|
if (symbolp(arg1) && symbolp(arg2)) return eqsymbol(arg1->name, arg2->name); // Same symbol?
|
|
if (arg1->cdr != arg2->cdr) return false; // Different values
|
|
#endif
|
|
if (integerp(arg1) && integerp(arg2)) return true; // Same integer
|
|
if (floatp(arg1) && floatp(arg2)) return true; // Same float
|
|
if (characterp(arg1) && characterp(arg2)) return true; // Same character
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
equal - implements Lisp equal
|
|
*/
|
|
bool equal (object *arg1, object *arg2) {
|
|
if (stringp(arg1) && stringp(arg2)) return (stringcompare(cons(arg1, cons(arg2, nil)), false, false, true) != -1);
|
|
if (consp(arg1) && consp(arg2)) return (equal(car(arg1), car(arg2)) && equal(cdr(arg1), cdr(arg2)));
|
|
return eq(arg1, arg2);
|
|
}
|
|
|
|
/*
|
|
listlength - returns the length of a list
|
|
*/
|
|
int listlength (object *list) {
|
|
int length = 0;
|
|
while (list != NULL) {
|
|
if (improperp(list)) error2(notproper);
|
|
list = cdr(list);
|
|
length++;
|
|
}
|
|
return length;
|
|
}
|
|
|
|
/*
|
|
checkarguments - checks the arguments list in a special form such as with-xxx,
|
|
dolist, or dotimes.
|
|
*/
|
|
object *checkarguments (object *args, int min, int max) {
|
|
if (args == NULL) error2(noargument);
|
|
args = first(args);
|
|
if (!listp(args)) error(notalist, args);
|
|
int length = listlength(args);
|
|
if (length < min) error(toofewargs, args);
|
|
if (length > max) error(toomanyargs, args);
|
|
return args;
|
|
}
|
|
|
|
// Mathematical helper functions
|
|
|
|
/*
|
|
add_floats - used by fn_add
|
|
Converts the numbers in args to floats, adds them to fresult, and returns the sum as a Lisp float.
|
|
*/
|
|
object *add_floats (object *args, float fresult) {
|
|
while (args != NULL) {
|
|
object *arg = car(args);
|
|
fresult = fresult + checkintfloat(arg);
|
|
args = cdr(args);
|
|
}
|
|
return makefloat(fresult);
|
|
}
|
|
|
|
/*
|
|
subtract_floats - used by fn_subtract with more than one argument
|
|
Converts the numbers in args to floats, subtracts them from fresult, and returns the result as a Lisp float.
|
|
*/
|
|
object *subtract_floats (object *args, float fresult) {
|
|
while (args != NULL) {
|
|
object *arg = car(args);
|
|
fresult = fresult - checkintfloat(arg);
|
|
args = cdr(args);
|
|
}
|
|
return makefloat(fresult);
|
|
}
|
|
|
|
/*
|
|
negate - used by fn_subtract with one argument
|
|
If the result is an integer, and negating it doesn't overflow, keep the result as an integer.
|
|
Otherwise convert the result to a float, negate it, and return the result as a Lisp float.
|
|
*/
|
|
object *negate (object *arg) {
|
|
if (integerp(arg)) {
|
|
int result = arg->integer;
|
|
if (result == INT_MIN) return makefloat(-result);
|
|
else return number(-result);
|
|
} else if (floatp(arg)) return makefloat(-(arg->single_float));
|
|
else error(notanumber, arg);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
multiply_floats - used by fn_multiply
|
|
Converts the numbers in args to floats, adds them to fresult, and returns the result as a Lisp float.
|
|
*/
|
|
object *multiply_floats (object *args, float fresult) {
|
|
while (args != NULL) {
|
|
object *arg = car(args);
|
|
fresult = fresult * checkintfloat(arg);
|
|
args = cdr(args);
|
|
}
|
|
return makefloat(fresult);
|
|
}
|
|
|
|
/*
|
|
divide_floats - used by fn_divide
|
|
Converts the numbers in args to floats, divides fresult by them, and returns the result as a Lisp float.
|
|
*/
|
|
object *divide_floats (object *args, float fresult) {
|
|
while (args != NULL) {
|
|
object *arg = car(args);
|
|
float f = checkintfloat(arg);
|
|
if (f == 0.0) error2(divisionbyzero);
|
|
fresult = fresult / f;
|
|
args = cdr(args);
|
|
}
|
|
return makefloat(fresult);
|
|
}
|
|
|
|
/*
|
|
remmod - implements rem (mod = false) and mod (mod = true).
|
|
*/
|
|
object *remmod (object *args, bool mod) {
|
|
object *arg1 = first(args);
|
|
object *arg2 = second(args);
|
|
if (integerp(arg1) && integerp(arg2)) {
|
|
int divisor = arg2->integer;
|
|
if (divisor == 0) error2(divisionbyzero);
|
|
int dividend = arg1->integer;
|
|
int remainder = dividend % divisor;
|
|
if (mod && (dividend<0) != (divisor<0)) remainder = remainder + divisor;
|
|
return number(remainder);
|
|
} else {
|
|
float fdivisor = checkintfloat(arg2);
|
|
if (fdivisor == 0.0) error2(divisionbyzero);
|
|
float fdividend = checkintfloat(arg1);
|
|
float fremainder = fmod(fdividend , fdivisor);
|
|
if (mod && (fdividend<0) != (fdivisor<0)) fremainder = fremainder + fdivisor;
|
|
return makefloat(fremainder);
|
|
}
|
|
}
|
|
|
|
/*
|
|
compare - a generic compare function
|
|
Used to implement the other comparison functions.
|
|
If lt is true the result is true if each argument is less than the next argument.
|
|
If gt is true the result is true if each argument is greater than the next argument.
|
|
If eq is true the result is true if each argument is equal to the next argument.
|
|
*/
|
|
object *compare (object *args, bool lt, bool gt, bool eq) {
|
|
object *arg1 = first(args);
|
|
args = cdr(args);
|
|
while (args != NULL) {
|
|
object *arg2 = first(args);
|
|
if (integerp(arg1) && integerp(arg2)) {
|
|
if (!lt && ((arg1->integer) < (arg2->integer))) return nil;
|
|
if (!eq && ((arg1->integer) == (arg2->integer))) return nil;
|
|
if (!gt && ((arg1->integer) > (arg2->integer))) return nil;
|
|
} else {
|
|
if (!lt && (checkintfloat(arg1) < checkintfloat(arg2))) return nil;
|
|
if (!eq && (checkintfloat(arg1) == checkintfloat(arg2))) return nil;
|
|
if (!gt && (checkintfloat(arg1) > checkintfloat(arg2))) return nil;
|
|
}
|
|
arg1 = arg2;
|
|
args = cdr(args);
|
|
}
|
|
return tee;
|
|
}
|
|
|
|
/*
|
|
intpower - calculates base to the power exp as an integer
|
|
*/
|
|
int intpower (int base, int exp) {
|
|
int result = 1;
|
|
while (exp) {
|
|
if (exp & 1) result = result * base;
|
|
exp = exp / 2;
|
|
base = base * base;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Association lists
|
|
|
|
/*
|
|
testargument - handles the :test argument for functions that accept it
|
|
*/
|
|
object *testargument (object *args) {
|
|
object *test = bsymbol(EQ);
|
|
if (args != NULL) {
|
|
if (cdr(args) == NULL) error2("unpaired keyword");
|
|
if ((isbuiltin(first(args), TEST))) test = second(args);
|
|
else error("unsupported keyword", first(args));
|
|
}
|
|
return test;
|
|
}
|
|
|
|
/*
|
|
delassoc - deletes the pair matching key from an association list and returns the key, or nil if not found
|
|
*/
|
|
object *delassoc (object *key, object **alist) {
|
|
object *list = *alist;
|
|
object *prev = NULL;
|
|
while (list != NULL) {
|
|
object *pair = first(list);
|
|
if (eq(key,car(pair))) {
|
|
if (prev == NULL) *alist = cdr(list);
|
|
else cdr(prev) = cdr(list);
|
|
return key;
|
|
}
|
|
prev = list;
|
|
list = cdr(list);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
// Array utilities
|
|
|
|
/*
|
|
nextpower2 - returns the smallest power of 2 that is equal to or greater than n
|
|
*/
|
|
int nextpower2 (int n) {
|
|
n--; n |= n >> 1; n |= n >> 2; n |= n >> 4;
|
|
n |= n >> 8; n |= n >> 16; n++;
|
|
return n<2 ? 2 : n;
|
|
}
|
|
|
|
/*
|
|
buildarray - builds an array with n elements using a tree of size s which must be a power of 2
|
|
The elements are initialised to the default def
|
|
*/
|
|
object *buildarray (int n, int s, object *def) {
|
|
int s2 = s>>1;
|
|
if (s2 == 1) {
|
|
if (n == 2) return cons(def, def);
|
|
else if (n == 1) return cons(def, NULL);
|
|
else return NULL;
|
|
} else if (n >= s2) return cons(buildarray(s2, s2, def), buildarray(n - s2, s2, def));
|
|
else return cons(buildarray(n, s2, def), nil);
|
|
}
|
|
|
|
object *makearray (object *dims, object *def, bool bitp) {
|
|
int size = 1;
|
|
object *dimensions = dims;
|
|
while (dims != NULL) {
|
|
int d = car(dims)->integer;
|
|
if (d < 0) error2("dimension can't be negative");
|
|
size = size * d;
|
|
dims = cdr(dims);
|
|
}
|
|
// Bit array identified by making first dimension negative
|
|
if (bitp) {
|
|
size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8);
|
|
car(dimensions) = number(-(car(dimensions)->integer));
|
|
}
|
|
object *ptr = myalloc();
|
|
ptr->type = ARRAY;
|
|
object *tree = nil;
|
|
if (size != 0) tree = buildarray(size, nextpower2(size), def);
|
|
ptr->cdr = cons(tree, dimensions);
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
arrayref - returns a pointer to the element specified by index in the array of size s
|
|
*/
|
|
object **arrayref (object *array, int index, int size) {
|
|
int mask = nextpower2(size)>>1;
|
|
object **p = &car(cdr(array));
|
|
while (mask) {
|
|
if ((index & mask) == 0) p = &(car(*p)); else p = &(cdr(*p));
|
|
mask = mask>>1;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
getarray - gets a pointer to an element in a multi-dimensional array, given a list of the subscripts subs
|
|
If the first subscript is negative it's a bit array and bit is set to the bit number
|
|
*/
|
|
object **getarray (object *array, object *subs, object *env, int *bit) {
|
|
int index = 0, size = 1, s;
|
|
*bit = -1;
|
|
bool bitp = false;
|
|
object *dims = cddr(array);
|
|
while (dims != NULL && subs != NULL) {
|
|
int d = car(dims)->integer;
|
|
if (d < 0) { d = -d; bitp = true; }
|
|
if (env) s = checkinteger(eval(car(subs), env)); else s = checkinteger(car(subs));
|
|
if (s < 0 || s >= d) error("subscript out of range", car(subs));
|
|
size = size * d;
|
|
index = index * d + s;
|
|
dims = cdr(dims); subs = cdr(subs);
|
|
}
|
|
if (dims != NULL) error2("too few subscripts");
|
|
if (subs != NULL) error2("too many subscripts");
|
|
if (bitp) {
|
|
size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8);
|
|
*bit = index & (sizeof(int)==4 ? 0x1F : 0x0F);
|
|
index = index>>(sizeof(int)==4 ? 5 : 4);
|
|
}
|
|
return arrayref(array, index, size);
|
|
}
|
|
|
|
/*
|
|
rslice - reads a slice of an array recursively
|
|
*/
|
|
void rslice (object *array, int size, int slice, object *dims, object *args) {
|
|
int d = first(dims)->integer;
|
|
for (int i = 0; i < d; i++) {
|
|
int index = slice * d + i;
|
|
if (!consp(args)) error2("initial contents don't match array type");
|
|
if (cdr(dims) == NULL) {
|
|
object **p = arrayref(array, index, size);
|
|
*p = car(args);
|
|
} else rslice(array, size, index, cdr(dims), car(args));
|
|
args = cdr(args);
|
|
}
|
|
}
|
|
|
|
/*
|
|
readarray - reads a list structure from args and converts it to a d-dimensional array.
|
|
Uses rslice for each of the slices of the array.
|
|
*/
|
|
object *readarray (int d, object *args) {
|
|
object *list = args;
|
|
object *dims = NULL; object *head = NULL;
|
|
int size = 1;
|
|
for (int i = 0; i < d; i++) {
|
|
if (!listp(list)) error2("initial contents don't match array type");
|
|
int l = listlength(list);
|
|
if (dims == NULL) { dims = cons(number(l), NULL); head = dims; }
|
|
else { cdr(dims) = cons(number(l), NULL); dims = cdr(dims); }
|
|
size = size * l;
|
|
if (list != NULL) list = car(list);
|
|
}
|
|
object *array = makearray(head, NULL, false);
|
|
rslice(array, size, 0, head, args);
|
|
return array;
|
|
}
|
|
|
|
/*
|
|
readbitarray - reads an item in the format #*1010101000110 by reading it and returning a list of integers,
|
|
and then converting that to a bit array
|
|
*/
|
|
object *readbitarray (gfun_t gfun) {
|
|
char ch = gfun();
|
|
object *head = NULL;
|
|
object *tail = NULL;
|
|
while (!issp(ch) && !isbr(ch)) {
|
|
if (ch != '0' && ch != '1') error2("illegal character in bit array");
|
|
object *cell = cons(number(ch - '0'), NULL);
|
|
if (head == NULL) head = cell;
|
|
else tail->cdr = cell;
|
|
tail = cell;
|
|
ch = gfun();
|
|
}
|
|
LastChar = ch;
|
|
int size = listlength(head);
|
|
object *array = makearray(cons(number(size), NULL), number(0), true);
|
|
size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8);
|
|
int index = 0;
|
|
while (head != NULL) {
|
|
object **loc = arrayref(array, index>>(sizeof(int)==4 ? 5 : 4), size);
|
|
int bit = index & (sizeof(int)==4 ? 0x1F : 0x0F);
|
|
*loc = number((((*loc)->integer) & ~(1<<bit)) | (car(head)->integer)<<bit);
|
|
index++;
|
|
head = cdr(head);
|
|
}
|
|
return array;
|
|
}
|
|
|
|
/*
|
|
pslice - prints a slice of an array recursively
|
|
*/
|
|
void pslice (object *array, int size, int slice, object *dims, pfun_t pfun, bool bitp) {
|
|
bool spaces = true;
|
|
if (slice == -1) { spaces = false; slice = 0; }
|
|
int d = first(dims)->integer;
|
|
if (d < 0) d = -d;
|
|
for (int i = 0; i < d; i++) {
|
|
if (i && spaces) pfun(' ');
|
|
int index = slice * d + i;
|
|
if (cdr(dims) == NULL) {
|
|
if (bitp) pint(((*arrayref(array, index>>(sizeof(int)==4 ? 5 : 4), size))->integer)>>
|
|
(index & (sizeof(int)==4 ? 0x1F : 0x0F)) & 1, pfun);
|
|
else printobject(*arrayref(array, index, size), pfun);
|
|
} else { pfun('('); pslice(array, size, index, cdr(dims), pfun, bitp); pfun(')'); }
|
|
}
|
|
}
|
|
|
|
/*
|
|
printarray - prints an array in the appropriate Lisp format
|
|
*/
|
|
void printarray (object *array, pfun_t pfun) {
|
|
object *dimensions = cddr(array);
|
|
object *dims = dimensions;
|
|
bool bitp = false;
|
|
int size = 1, n = 0;
|
|
while (dims != NULL) {
|
|
int d = car(dims)->integer;
|
|
if (d < 0) { bitp = true; d = -d; }
|
|
size = size * d;
|
|
dims = cdr(dims); n++;
|
|
}
|
|
if (bitp) size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8);
|
|
pfun('#');
|
|
if (n == 1 && bitp) { pfun('*'); pslice(array, size, -1, dimensions, pfun, bitp); }
|
|
else {
|
|
if (n > 1) { pint(n, pfun); pfun('A'); }
|
|
pfun('('); pslice(array, size, 0, dimensions, pfun, bitp); pfun(')');
|
|
}
|
|
}
|
|
|
|
// String utilities
|
|
|
|
void indent (uint8_t spaces, char ch, pfun_t pfun) {
|
|
for (uint8_t i=0; i<spaces; i++) pfun(ch);
|
|
}
|
|
|
|
/*
|
|
startstring - starts building a string
|
|
*/
|
|
object *startstring () {
|
|
object *string = newstring();
|
|
GlobalString = string;
|
|
GlobalStringTail = string;
|
|
return string;
|
|
}
|
|
|
|
/*
|
|
princtostring - implements Lisp princtostring function
|
|
*/
|
|
object *princtostring (object *arg) {
|
|
object *obj = startstring();
|
|
prin1object(arg, pstr);
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
buildstring - adds a character on the end of a string
|
|
Handles Lisp strings packed four characters per 32-bit word
|
|
*/
|
|
void buildstring (char ch, object** tail) {
|
|
object* cell;
|
|
if (cdr(*tail) == NULL) {
|
|
cell = myalloc(); cdr(*tail) = cell;
|
|
} else if (((*tail)->chars & 0xFFFFFF) == 0) {
|
|
(*tail)->chars |= ch<<16; return;
|
|
} else if (((*tail)->chars & 0xFFFF) == 0) {
|
|
(*tail)->chars |= ch<<8; return;
|
|
} else if (((*tail)->chars & 0xFF) == 0) {
|
|
(*tail)->chars |= ch; return;
|
|
} else {
|
|
cell = myalloc(); car(*tail) = cell;
|
|
}
|
|
car(cell) = NULL; cell->chars = ch<<24; *tail = cell;
|
|
}
|
|
|
|
/*
|
|
copystring - returns a copy of a Lisp string
|
|
*/
|
|
object *copystring (object *arg) {
|
|
object *obj = newstring();
|
|
object *ptr = obj;
|
|
arg = cdr(arg);
|
|
while (arg != NULL) {
|
|
object *cell = myalloc(); car(cell) = NULL;
|
|
if (cdr(obj) == NULL) cdr(obj) = cell; else car(ptr) = cell;
|
|
ptr = cell;
|
|
ptr->chars = arg->chars;
|
|
arg = car(arg);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
readstring - reads characters from an input stream up to delimiter delim
|
|
and returns a Lisp string
|
|
*/
|
|
object *readstring (uint8_t delim, bool esc, gfun_t gfun) {
|
|
object *obj = newstring();
|
|
object *tail = obj;
|
|
int ch = gfun();
|
|
if (ch == -1) return nil;
|
|
while ((ch != delim) && (ch != -1)) {
|
|
if (esc && ch == '\\') ch = gfun();
|
|
buildstring(ch, &tail);
|
|
ch = gfun();
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
stringlength - returns the length of a Lisp string
|
|
Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word
|
|
*/
|
|
int stringlength (object *form) {
|
|
int length = 0;
|
|
form = cdr(form);
|
|
while (form != NULL) {
|
|
int chars = form->chars;
|
|
for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) {
|
|
if (chars>>i & 0xFF) length++;
|
|
}
|
|
form = car(form);
|
|
}
|
|
return length;
|
|
}
|
|
|
|
/*
|
|
getcharplace - gets character n in a Lisp string, and sets shift to (- the shift position -2)
|
|
Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word.
|
|
*/
|
|
object **getcharplace (object *string, int n, int *shift) {
|
|
object **arg = &cdr(string);
|
|
int top;
|
|
if (sizeof(int) == 4) { top = n>>2; *shift = 3 - (n&3); }
|
|
else { top = n>>1; *shift = 1 - (n&1); }
|
|
*shift = - (*shift + 2);
|
|
for (int i=0; i<top; i++) {
|
|
if (*arg == NULL) break;
|
|
arg = &car(*arg);
|
|
}
|
|
return arg;
|
|
}
|
|
|
|
/*
|
|
nthchar - returns the nth character from a Lisp string
|
|
*/
|
|
uint8_t nthchar (object *string, int n) {
|
|
int shift;
|
|
object **arg = getcharplace(string, n, &shift);
|
|
if (*arg == NULL) return 0;
|
|
return (((*arg)->chars)>>((-shift-2)<<3)) & 0xFF;
|
|
}
|
|
|
|
/*
|
|
gstr - reads a character from a string stream
|
|
*/
|
|
int gstr () {
|
|
if (LastChar) {
|
|
char temp = LastChar;
|
|
LastChar = 0;
|
|
return temp;
|
|
}
|
|
char c = nthchar(GlobalString, GlobalStringIndex++);
|
|
if (c != 0) return c;
|
|
return '\n'; // -1?
|
|
}
|
|
|
|
/*
|
|
pstr - prints a character to a string stream
|
|
*/
|
|
void pstr (char c) {
|
|
buildstring(c, &GlobalStringTail);
|
|
}
|
|
|
|
/*
|
|
lispstring - converts a C string to a Lisp string
|
|
*/
|
|
object *lispstring (char *s) {
|
|
object *obj = newstring();
|
|
object *tail = obj;
|
|
while(1) {
|
|
char ch = *s++;
|
|
if (ch == 0) break;
|
|
if (ch == '\\') ch = *s++;
|
|
buildstring(ch, &tail);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
stringcompare - a generic string compare function
|
|
Used to implement the other string comparison functions.
|
|
Returns -1 if the comparison is false, or the index of the first mismatch if it is true.
|
|
If lt is true the result is true if the first argument is less than the second argument.
|
|
If gt is true the result is true if the first argument is greater than the second argument.
|
|
If eq is true the result is true if the first argument is equal to the second argument.
|
|
*/
|
|
int stringcompare (object *args, bool lt, bool gt, bool eq) {
|
|
object *arg1 = checkstring(first(args));
|
|
object *arg2 = checkstring(second(args));
|
|
arg1 = cdr(arg1); arg2 = cdr(arg2);
|
|
int m = 0; chars_t a = 0, b = 0;
|
|
while ((arg1 != NULL) || (arg2 != NULL)) {
|
|
if (arg1 == NULL) return lt ? m : -1;
|
|
if (arg2 == NULL) return gt ? m : -1;
|
|
a = arg1->chars; b = arg2->chars;
|
|
if (a < b) { if (lt) { m = m + sizeof(int); while (a != b) { m--; a = a >> 8; b = b >> 8; } return m; } else return -1; }
|
|
if (a > b) { if (gt) { m = m + sizeof(int); while (a != b) { m--; a = a >> 8; b = b >> 8; } return m; } else return -1; }
|
|
arg1 = car(arg1); arg2 = car(arg2);
|
|
m = m + sizeof(int);
|
|
}
|
|
if (eq) { m = m - sizeof(int); while (a != 0) { m++; a = a << 8;} return m;} else return -1;
|
|
}
|
|
|
|
/*
|
|
documentation - returns the documentation string of a built-in or user-defined function.
|
|
*/
|
|
object *documentation (object *arg, object *env) {
|
|
if (arg == NULL) return nil;
|
|
if (!symbolp(arg)) error(notasymbol, arg);
|
|
object *pair = findpair(arg, env);
|
|
if (pair != NULL) {
|
|
object *val = cdr(pair);
|
|
if (listp(val) && first(val)->name == sym(LAMBDA) && cdr(val) != NULL && cddr(val) != NULL) {
|
|
if (stringp(third(val))) return third(val);
|
|
}
|
|
}
|
|
symbol_t docname = arg->name;
|
|
if (!builtinp(docname)) return nil;
|
|
char *docstring = lookupdoc(builtin(docname));
|
|
if (docstring == NULL) return nil;
|
|
object *obj = startstring();
|
|
pfstring(docstring, pstr);
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
apropos - finds the user-defined and built-in functions whose names contain the specified string or symbol,
|
|
and prints them if print is true, or returns them in a list.
|
|
*/
|
|
object *apropos (object *arg, bool print) {
|
|
char buf[17], buf2[33];
|
|
char *part = cstring(princtostring(arg), buf, 17);
|
|
object *result = cons(NULL, NULL);
|
|
object *ptr = result;
|
|
// User-defined?
|
|
object *globals = GlobalEnv;
|
|
while (globals != NULL) {
|
|
object *pair = first(globals);
|
|
object *var = car(pair);
|
|
object *val = cdr(pair);
|
|
char *full = cstring(princtostring(var), buf2, 33);
|
|
if (strstr(full, part) != NULL) {
|
|
if (print) {
|
|
printsymbol(var, pserial); pserial(' '); pserial('(');
|
|
if (consp(val) && isbuiltin(car(val), LAMBDA)) pfstring("user function", pserial);
|
|
else if (consp(val) && car(val)->type == CODE) pfstring("code", pserial);
|
|
else pfstring("user symbol", pserial);
|
|
pserial(')'); pln(pserial);
|
|
} else {
|
|
cdr(ptr) = cons(var, NULL); ptr = cdr(ptr);
|
|
}
|
|
}
|
|
globals = cdr(globals);
|
|
testescape();
|
|
}
|
|
// Built-in?
|
|
int entries = tablesize(0) + tablesize(1);
|
|
for (int i = 0; i < entries; i++) {
|
|
if (findsubstring(part, (builtin_t)i)) {
|
|
if (print) {
|
|
uint8_t fntype = fntype(i);
|
|
pbuiltin((builtin_t)i, pserial); pserial(' '); pserial('(');
|
|
if (fntype == FUNCTIONS) pfstring("function", pserial);
|
|
else if (fntype == SPECIAL_FORMS || fntype == TAIL_FORMS) pfstring("special form", pserial);
|
|
else pfstring("symbol/keyword", pserial);
|
|
pserial(')'); pln(pserial);
|
|
} else {
|
|
cdr(ptr) = cons(bsymbol(i), NULL); ptr = cdr(ptr);
|
|
}
|
|
}
|
|
testescape();
|
|
}
|
|
return cdr(result);
|
|
}
|
|
|
|
/*
|
|
cstring - converts a Lisp string to a C string in buffer and returns buffer
|
|
Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word
|
|
*/
|
|
char *cstring (object *form, char *buffer, int buflen) {
|
|
form = cdr(checkstring(form));
|
|
int index = 0;
|
|
while (form != NULL) {
|
|
int chars = form->integer;
|
|
for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) {
|
|
char ch = chars>>i & 0xFF;
|
|
if (ch) {
|
|
if (index >= buflen-1) error2("no room for string");
|
|
buffer[index++] = ch;
|
|
}
|
|
}
|
|
form = car(form);
|
|
}
|
|
buffer[index] = '\0';
|
|
return buffer;
|
|
}
|
|
|
|
/*
|
|
iptostring - converts a 32-bit IP address to a lisp string
|
|
*/
|
|
object *iptostring (uint32_t ip) {
|
|
union { uint32_t data2; uint8_t u8[4]; };
|
|
object *obj = startstring();
|
|
data2 = ip;
|
|
for (int i=0; i<4; i++) {
|
|
if (i) pstr('.');
|
|
pintbase(u8[i], 10, pstr);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
ipstring - parses an IP address from a Lisp string and returns it as an IPAddress type (uint32_t)
|
|
Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word
|
|
*/
|
|
uint32_t ipstring (object *form) {
|
|
form = cdr(checkstring(form));
|
|
int p = 0;
|
|
union { uint32_t ipaddress; uint8_t ipbytes[4]; } ;
|
|
ipaddress = 0;
|
|
while (form != NULL) {
|
|
int chars = form->integer;
|
|
for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) {
|
|
char ch = chars>>i & 0xFF;
|
|
if (ch) {
|
|
if (ch == '.') { p++; if (p > 3) error2("illegal IP address"); }
|
|
else ipbytes[p] = (ipbytes[p] * 10) + ch - '0';
|
|
}
|
|
}
|
|
form = car(form);
|
|
}
|
|
return ipaddress;
|
|
}
|
|
|
|
/*
|
|
value - lookup variable in environment, taking into account PSRAM
|
|
*/
|
|
object *value (symbol_t n, object *env) {
|
|
while (env != NULL) {
|
|
object *pair = car(env);
|
|
#if !defined(BOARD_HAS_PSRAM)
|
|
if (pair != NULL && car(pair)->name == n) return pair;
|
|
#else
|
|
if (pair != NULL && eqsymbol(car(pair)->name, n)) return pair;
|
|
#endif
|
|
env = cdr(env);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
findpair - returns the (var . value) pair bound to variable var in the local or global environment
|
|
*/
|
|
object *findpair (object *var, object *env) {
|
|
symbol_t name = var->name;
|
|
object *pair = value(name, env);
|
|
if (pair == NULL) pair = value(name, GlobalEnv);
|
|
return pair;
|
|
}
|
|
|
|
/*
|
|
boundp - tests whether var is bound to a value
|
|
*/
|
|
bool boundp (object *var, object *env) {
|
|
if (!symbolp(var)) error(notasymbol, var);
|
|
return (findpair(var, env) != NULL);
|
|
}
|
|
|
|
/*
|
|
findvalue - returns the value bound to variable var, or gives an error if unbound
|
|
*/
|
|
object *findvalue (object *var, object *env) {
|
|
object *pair = findpair(var, env);
|
|
if (pair == NULL) error("unknown variable", var);
|
|
return pair;
|
|
}
|
|
|
|
// Handling closures
|
|
|
|
object *closure (int tc, symbol_t name, object *function, object *args, object **env) {
|
|
object *state = car(function);
|
|
function = cdr(function);
|
|
int trace = tracing(name);
|
|
if (trace) {
|
|
indent(TraceDepth[trace-1]<<1, ' ', pserial);
|
|
pint(TraceDepth[trace-1]++, pserial);
|
|
pserial(':'); pserial(' '); pserial('('); printsymbol(symbol(name), pserial);
|
|
}
|
|
object *params = first(function);
|
|
if (!listp(params)) errorsym(name, notalist, params);
|
|
function = cdr(function);
|
|
// Dropframe
|
|
if (tc) {
|
|
if (*env != NULL && car(*env) == NULL) {
|
|
pop(*env);
|
|
while (*env != NULL && car(*env) != NULL) pop(*env);
|
|
} else push(nil, *env);
|
|
}
|
|
// Push state
|
|
while (consp(state)) {
|
|
object *pair = first(state);
|
|
push(pair, *env);
|
|
state = cdr(state);
|
|
}
|
|
// Add arguments to environment
|
|
bool optional = false;
|
|
while (params != NULL) {
|
|
object *value;
|
|
object *var = first(params);
|
|
if (isbuiltin(var, OPTIONAL)) optional = true;
|
|
else {
|
|
if (consp(var)) {
|
|
if (!optional) errorsym(name, "invalid default value", var);
|
|
if (args == NULL) value = eval(second(var), *env);
|
|
else { value = first(args); args = cdr(args); }
|
|
var = first(var);
|
|
if (!symbolp(var)) errorsym(name, "illegal optional parameter", var);
|
|
} else if (!symbolp(var)) {
|
|
errorsym(name, "illegal function parameter", var);
|
|
} else if (isbuiltin(var, AMPREST)) {
|
|
params = cdr(params);
|
|
var = first(params);
|
|
value = args;
|
|
args = NULL;
|
|
} else {
|
|
if (args == NULL) {
|
|
if (optional) value = nil;
|
|
else errorsym2(name, toofewargs);
|
|
} else { value = first(args); args = cdr(args); }
|
|
}
|
|
push(cons(var,value), *env);
|
|
if (trace) { pserial(' '); printobject(value, pserial); }
|
|
}
|
|
params = cdr(params);
|
|
}
|
|
if (args != NULL) errorsym2(name, toomanyargs);
|
|
if (trace) { pserial(')'); pln(pserial); }
|
|
// Do an implicit progn
|
|
if (tc) push(nil, *env);
|
|
return tf_progn(function, *env);
|
|
}
|
|
|
|
object *apply (object *function, object *args, object *env) {
|
|
if (symbolp(function)) {
|
|
builtin_t fname = builtin(function->name);
|
|
if ((fname < ENDFUNCTIONS) && (fntype(fname) == FUNCTIONS)) {
|
|
Context = fname;
|
|
checkargs(args);
|
|
return ((fn_ptr_type)lookupfn(fname))(args, env);
|
|
} else function = eval(function, env);
|
|
}
|
|
if (consp(function) && isbuiltin(car(function), LAMBDA)) {
|
|
object *result = closure(0, sym(NIL), function, args, &env);
|
|
return eval(result, env);
|
|
}
|
|
if (consp(function) && isbuiltin(car(function), CLOSURE)) {
|
|
function = cdr(function);
|
|
object *result = closure(0, sym(NIL), function, args, &env);
|
|
return eval(result, env);
|
|
}
|
|
error(illegalfn, function);
|
|
return NULL;
|
|
}
|
|
|
|
// In-place operations
|
|
|
|
/*
|
|
place - returns a pointer to an object referenced in the second argument of an
|
|
in-place operation such as setf. bit is used to indicate the bit position in a bit array
|
|
*/
|
|
object **place (object *args, object *env, int *bit) {
|
|
*bit = -1;
|
|
if (atom(args)) return &cdr(findvalue(args, env));
|
|
object* function = first(args);
|
|
if (symbolp(function)) {
|
|
symbol_t sname = function->name;
|
|
if (sname == sym(CAR) || sname == sym(FIRST)) {
|
|
object *value = eval(second(args), env);
|
|
if (!listp(value)) error(canttakecar, value);
|
|
return &car(value);
|
|
}
|
|
if (sname == sym(CDR) || sname == sym(REST)) {
|
|
object *value = eval(second(args), env);
|
|
if (!listp(value)) error(canttakecdr, value);
|
|
return &cdr(value);
|
|
}
|
|
if (sname == sym(NTH)) {
|
|
int index = checkinteger(eval(second(args), env));
|
|
object *list = eval(third(args), env);
|
|
if (atom(list)) { Context = NTH; error("second argument is not a list", list); }
|
|
int i = index;
|
|
while (i > 0) {
|
|
list = cdr(list);
|
|
if (list == NULL) { Context = NTH; error(indexrange, number(index)); }
|
|
i--;
|
|
}
|
|
return &car(list);
|
|
}
|
|
if (sname == sym(CHAR)) {
|
|
int index = checkinteger(eval(third(args), env));
|
|
object *string = checkstring(eval(second(args), env));
|
|
object **loc = getcharplace(string, index, bit);
|
|
if ((*loc) == NULL || (((((*loc)->chars)>>((-(*bit)-2)<<3)) & 0xFF) == 0)) { Context = CHAR; error(indexrange, number(index)); }
|
|
return loc;
|
|
}
|
|
if (sname == sym(AREF)) {
|
|
object *array = eval(second(args), env);
|
|
if (!arrayp(array)) { Context = AREF; error("first argument is not an array", array); }
|
|
return getarray(array, cddr(args), env, bit);
|
|
}
|
|
}
|
|
error2("illegal place");
|
|
return nil;
|
|
}
|
|
|
|
// Checked car and cdr
|
|
|
|
/*
|
|
carx - car with error checking
|
|
*/
|
|
object *carx (object *arg) {
|
|
if (!listp(arg)) error(canttakecar, arg);
|
|
if (arg == nil) return nil;
|
|
return car(arg);
|
|
}
|
|
|
|
/*
|
|
cdrx - cdr with error checking
|
|
*/
|
|
object *cdrx (object *arg) {
|
|
if (!listp(arg)) error(canttakecdr, arg);
|
|
if (arg == nil) return nil;
|
|
return cdr(arg);
|
|
}
|
|
|
|
/*
|
|
cxxxr - implements a general cxxxr function,
|
|
pattern is a sequence of bits 0b1xxx where x is 0 for a and 1 for d.
|
|
*/
|
|
object *cxxxr (object *args, uint8_t pattern) {
|
|
object *arg = first(args);
|
|
while (pattern != 1) {
|
|
if ((pattern & 1) == 0) arg = carx(arg); else arg = cdrx(arg);
|
|
pattern = pattern>>1;
|
|
}
|
|
return arg;
|
|
}
|
|
|
|
// Mapping helper functions
|
|
|
|
/*
|
|
mapcl - handles either mapc when mapl=false, or mapl when mapl=true
|
|
*/
|
|
object *mapcl (object *args, object *env, bool mapl) {
|
|
object *function = first(args);
|
|
args = cdr(args);
|
|
object *result = first(args);
|
|
protect(result);
|
|
object *params = cons(NULL, NULL);
|
|
protect(params);
|
|
// Make parameters
|
|
while (true) {
|
|
object *tailp = params;
|
|
object *lists = args;
|
|
while (lists != NULL) {
|
|
object *list = car(lists);
|
|
if (list == NULL) {
|
|
unprotect(); unprotect();
|
|
return result;
|
|
}
|
|
if (improperp(list)) error(notproper, list);
|
|
object *item = mapl ? list : first(list);
|
|
object *obj = cons(item, NULL);
|
|
car(lists) = cdr(list);
|
|
cdr(tailp) = obj; tailp = obj;
|
|
lists = cdr(lists);
|
|
}
|
|
apply(function, cdr(params), env);
|
|
}
|
|
}
|
|
|
|
/*
|
|
mapcarfun - function specifying how to combine the results in mapcar
|
|
*/
|
|
void mapcarfun (object *result, object **tail) {
|
|
object *obj = cons(result,NULL);
|
|
cdr(*tail) = obj; *tail = obj;
|
|
}
|
|
|
|
/*
|
|
mapcanfun - function specifying how to combine the results in mapcan
|
|
*/
|
|
void mapcanfun (object *result, object **tail) {
|
|
if (cdr(*tail) != NULL) error(notproper, *tail);
|
|
while (consp(result)) {
|
|
cdr(*tail) = result; *tail = result;
|
|
result = cdr(result);
|
|
}
|
|
}
|
|
|
|
/*
|
|
mapcarcan - function used by marcar and mapcan when maplist=false, and maplist when maplist=true
|
|
It takes the arguments, the env, a function specifying how the results are combined, and a bool.
|
|
*/
|
|
object *mapcarcan (object *args, object *env, mapfun_t fun, bool maplist) {
|
|
object *function = first(args);
|
|
args = cdr(args);
|
|
object *params = cons(NULL, NULL);
|
|
protect(params);
|
|
object *head = cons(NULL, NULL);
|
|
protect(head);
|
|
object *tail = head;
|
|
// Make parameters
|
|
while (true) {
|
|
object *tailp = params;
|
|
object *lists = args;
|
|
while (lists != NULL) {
|
|
object *list = car(lists);
|
|
if (list == NULL) {
|
|
unprotect(); unprotect();
|
|
return cdr(head);
|
|
}
|
|
if (improperp(list)) error(notproper, list);
|
|
object *item = maplist ? list : first(list);
|
|
object *obj = cons(item, NULL);
|
|
car(lists) = cdr(list);
|
|
cdr(tailp) = obj; tailp = obj;
|
|
lists = cdr(lists);
|
|
}
|
|
object *result = apply(function, cdr(params), env);
|
|
fun(result, &tail);
|
|
}
|
|
}
|
|
|
|
/*
|
|
dobody - function used by do when star=false and do* when star=true
|
|
*/
|
|
object *dobody (object *args, object *env, bool star) {
|
|
object *varlist = first(args), *endlist = second(args);
|
|
object *head = cons(NULL, NULL);
|
|
protect(head);
|
|
object *ptr = head;
|
|
object *newenv = env;
|
|
while (varlist != NULL) {
|
|
object *varform = first(varlist);
|
|
object *var, *init = NULL, *step = NULL;
|
|
if (atom(varform)) var = varform;
|
|
else {
|
|
var = first(varform);
|
|
varform = cdr(varform);
|
|
if (varform != NULL) {
|
|
init = eval(first(varform), env);
|
|
varform = cdr(varform);
|
|
if (varform != NULL) step = cons(first(varform), NULL);
|
|
}
|
|
}
|
|
object *pair = cons(var, init);
|
|
push(pair, newenv);
|
|
if (star) env = newenv;
|
|
object *cell = cons(cons(step, pair), NULL);
|
|
cdr(ptr) = cell; ptr = cdr(ptr);
|
|
varlist = cdr(varlist);
|
|
}
|
|
env = newenv;
|
|
head = cdr(head);
|
|
object *endtest = first(endlist), *results = cdr(endlist);
|
|
while (eval(endtest, env) == NULL) {
|
|
object *forms = cddr(args);
|
|
while (forms != NULL) {
|
|
object *result = eval(car(forms), env);
|
|
if (tstflag(RETURNFLAG)) {
|
|
clrflag(RETURNFLAG);
|
|
return result;
|
|
}
|
|
forms = cdr(forms);
|
|
}
|
|
object *varlist = head;
|
|
int count = 0;
|
|
while (varlist != NULL) {
|
|
object *varform = first(varlist);
|
|
object *step = car(varform), *pair = cdr(varform);
|
|
if (step != NULL) {
|
|
object *val = eval(first(step), env);
|
|
if (star) {
|
|
cdr(pair) = val;
|
|
} else {
|
|
push(val, GCStack);
|
|
push(pair, GCStack);
|
|
count++;
|
|
}
|
|
}
|
|
varlist = cdr(varlist);
|
|
}
|
|
while (count > 0) {
|
|
cdr(car(GCStack)) = car(cdr(GCStack));
|
|
pop(GCStack); pop(GCStack);
|
|
count--;
|
|
}
|
|
}
|
|
unprotect();
|
|
return eval(tf_progn(results, env), env);
|
|
}
|
|
|
|
// I2C interface for up to two ports, using Arduino Wire: in
|
|
// picocalc, i2c0 is for keyboard.
|
|
void I2Cinit (TwoWire *port, bool enablePullup) {
|
|
(void) enablePullup;
|
|
#if defined(ULISP_I2C1)
|
|
port->begin();
|
|
#endif
|
|
}
|
|
|
|
int I2Cread (TwoWire *port) {
|
|
#if defined(ULISP_I2C1)
|
|
return port->read();
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void I2Cwrite (TwoWire *port, uint8_t data) {
|
|
#if defined(ULISP_I2C1)
|
|
port->write(data);
|
|
#endif
|
|
}
|
|
|
|
bool I2Cstart (TwoWire *port, uint8_t address, uint8_t read) {
|
|
#if defined(ULISP_I2C1)
|
|
int ok = true;
|
|
if (read == 0) {
|
|
port->beginTransmission(address);
|
|
ok = (port->endTransmission(true) == 0);
|
|
port->beginTransmission(address);
|
|
}
|
|
else port->requestFrom(address, I2Ccount);
|
|
return ok;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool I2Crestart (TwoWire *port, uint8_t address, uint8_t read) {
|
|
#if defined(ULISP_I2C1)
|
|
int error = (port->endTransmission(false) != 0);
|
|
if (read == 0) port->beginTransmission(address);
|
|
else port->requestFrom(address, I2Ccount);
|
|
return error ? false : true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void I2Cstop (TwoWire *port, uint8_t read) {
|
|
#if defined(ULISP_I2C1)
|
|
if (read == 0) port->endTransmission(); // Check for error?
|
|
// Release pins
|
|
port->end();
|
|
#endif
|
|
}
|
|
|
|
// Streams
|
|
|
|
// Simplify board differences
|
|
#if defined(ARDUINO_NRF52840_CLUE) || defined(ARDUINO_GRAND_CENTRAL_M4) \
|
|
|| defined(ARDUINO_PYBADGE_M4) || defined(ARDUINO_PYGAMER_M4) || defined(ARDUINO_TEENSY40) \
|
|
|| defined(ARDUINO_TEENSY41) || defined(ARDUINO_RASPBERRY_PI_PICO) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO_W) || defined(ARDUINO_RASPBERRY_PI_PICO_2) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO_2W) || defined(ARDUINO_PIMORONI_PICO_PLUS_2)
|
|
#define ULISP_SPI1
|
|
#endif
|
|
#if defined(ARDUINO_WIO_TERMINAL) || defined(ARDUINO_BBC_MICROBIT_V2) \
|
|
|| defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(MAX32620) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \
|
|
|| defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) \
|
|
|| defined(ARDUINO_PIMORONI_PICO_PLUS_2) || defined(ARDUINO_GRAND_CENTRAL_M4) \
|
|
|| defined(ARDUINO_NRF52840_CIRCUITPLAY)
|
|
#define ULISP_I2C1
|
|
#endif
|
|
#if defined(ARDUINO_SAM_DUE) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)
|
|
#define ULISP_SERIAL3
|
|
#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) \
|
|
|| defined(ARDUINO_PIMORONI_PICO_PLUS_2)
|
|
#define ULISP_SPI1
|
|
#define ULISP_SERIAL1
|
|
#elif !defined(CPU_NRF51822) && !defined(CPU_NRF52833) && !defined(ARDUINO_FEATHER_F405)
|
|
#define ULISP_SERIAL1
|
|
#endif
|
|
#if defined(ARDUINO_RASPBERRY_PI_PICO_W) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) \
|
|
|| defined(ARDUINO_UNOWIFIR4)
|
|
#define ULISP_WIFI
|
|
#endif
|
|
|
|
inline int spiread () { return SPI.transfer(0); }
|
|
#if defined(ULISP_SPI1)
|
|
inline int spi1read () { return SPI1.transfer(0); }
|
|
#endif
|
|
inline int i2cread () { return I2Cread(&Wire); }
|
|
#if defined(ULISP_I2C1)
|
|
inline int i2c1read () { return I2Cread(&Wire1); }
|
|
#endif
|
|
#if defined(ULISP_SERIAL3)
|
|
inline int serial3read () { while (!Serial3.available()) testescape(); return Serial3.read(); }
|
|
#endif
|
|
#if defined(ULISP_SERIAL3) || defined(ULISP_SERIAL2)
|
|
inline int serial2read () { while (!Serial2.available()) testescape(); return Serial2.read(); }
|
|
#endif
|
|
#if defined(ULISP_SERIAL3) || defined(ULISP_SERIAL2) || defined(ULISP_SERIAL1)
|
|
inline int serial1read () { while (!Serial1.available()) testescape(); return Serial1.read(); }
|
|
#endif
|
|
#if defined(sdcardsupport)
|
|
File SDpfile, SDgfile;
|
|
inline int SDread () {
|
|
if (LastChar) {
|
|
char temp = LastChar;
|
|
LastChar = 0;
|
|
return temp;
|
|
}
|
|
return SDgfile.read();
|
|
}
|
|
#endif
|
|
|
|
#if defined(ULISP_WIFI)
|
|
WiFiClient client;
|
|
WiFiServer server(80);
|
|
|
|
inline int WiFiread () {
|
|
if (LastChar) {
|
|
char temp = LastChar;
|
|
LastChar = 0;
|
|
return temp;
|
|
}
|
|
while (!server.accept()) testescape();
|
|
return client.read();
|
|
}
|
|
#endif
|
|
|
|
void serialbegin (int address, int baud) {
|
|
#if defined(ULISP_SERIAL3)
|
|
if (address == 1) Serial1.begin((long)baud*100);
|
|
else if (address == 2) Serial2.begin((long)baud*100);
|
|
else if (address == 3) Serial3.begin((long)baud*100);
|
|
#elif defined(ULISP_SERIAL2)
|
|
if (address == 1) Serial1.begin((long)baud*100);
|
|
else if (address == 2) Serial2.begin((long)baud*100);
|
|
#elif defined(ULISP_SERIAL1)
|
|
#if defined(PLATFORM_PICOCALC)
|
|
// Waveshare uses Serial1 for default.
|
|
if (address == 1) {}
|
|
#else
|
|
if (address == 1) Serial1.begin((long)baud*100);
|
|
#endif
|
|
#else
|
|
(void) baud;
|
|
if (false);
|
|
#endif
|
|
else error("port not supported", number(address));
|
|
}
|
|
|
|
void serialend (int address) {
|
|
#if defined(ULISP_SERIAL3)
|
|
if (address == 1) {Serial1.flush(); Serial1.end(); }
|
|
else if (address == 2) {Serial2.flush(); Serial2.end(); }
|
|
else if (address == 3) {Serial3.flush(); Serial3.end(); }
|
|
#elif defined(ULISP_SERIAL2)
|
|
if (address == 1) {Serial1.flush(); Serial1.end(); }
|
|
else if (address == 2) {Serial2.flush(); Serial2.end(); }
|
|
#elif defined(ULISP_SERIAL1)
|
|
#if defined(PLATFORM_PICOCALC)
|
|
if (address == 1) {Serial1.flush();}
|
|
#else
|
|
if (address == 1) {Serial1.flush(); Serial1.end(); }
|
|
#endif
|
|
|
|
#else
|
|
if (false);
|
|
#endif
|
|
else error("port not supported", number(address));
|
|
}
|
|
|
|
gfun_t gstreamfun (object *args) {
|
|
int streamtype = SERIALSTREAM;
|
|
int address = 0;
|
|
gfun_t gfun = gserial;
|
|
if (args != NULL) {
|
|
int stream = isstream(first(args));
|
|
streamtype = stream>>8; address = stream & 0xFF;
|
|
}
|
|
if (streamtype == I2CSTREAM) {
|
|
if (address < 128) gfun = i2cread;
|
|
#if defined(ULISP_I2C1)
|
|
else gfun = i2c1read;
|
|
#endif
|
|
} else if (streamtype == SPISTREAM) {
|
|
if (address < 128) gfun = spiread;
|
|
#if defined(ULISP_SPI1)
|
|
else gfun = spi1read;
|
|
#endif
|
|
}
|
|
else if (streamtype == SERIALSTREAM) {
|
|
if (address == 0) gfun = gserial;
|
|
#if defined(ULISP_SERIAL3)
|
|
else if (address == 1) gfun = serial1read;
|
|
else if (address == 2) gfun = serial2read;
|
|
else if (address == 3) gfun = serial3read;
|
|
#elif defined(ULISP_SERIAL2)
|
|
else if (address == 1) gfun = serial1read;
|
|
else if (address == 2) gfun = serial2read;
|
|
#elif defined(ULISP_SERIAL1)
|
|
else if (address == 1) gfun = serial1read;
|
|
#endif
|
|
}
|
|
#if defined(sdcardsupport)
|
|
else if (streamtype == SDSTREAM) gfun = (gfun_t)SDread;
|
|
#endif
|
|
#if defined(ULISP_WIFI)
|
|
else if (streamtype == WIFISTREAM) gfun = (gfun_t)WiFiread;
|
|
#endif
|
|
else error2("unknown stream type");
|
|
return gfun;
|
|
}
|
|
|
|
inline void spiwrite (char c) { SPI.transfer(c); }
|
|
#if defined(ULISP_SPI1)
|
|
inline void spi1write (char c) { SPI1.transfer(c); }
|
|
#endif
|
|
inline void i2cwrite (char c) { I2Cwrite(&Wire, c); }
|
|
#if defined(ULISP_I2C1)
|
|
inline void i2c1write (char c) { I2Cwrite(&Wire1, c); }
|
|
#endif
|
|
#if defined(ULISP_SERIAL3)
|
|
inline void serial1write (char c) { Serial1.write(c); }
|
|
inline void serial2write (char c) { Serial2.write(c); }
|
|
inline void serial3write (char c) { Serial3.write(c); }
|
|
#elif defined(ULISP_SERIAL2)
|
|
inline void serial2write (char c) { Serial2.write(c); }
|
|
inline void serial1write (char c) { Serial1.write(c); }
|
|
#elif defined(ULISP_SERIAL1)
|
|
inline void serial1write (char c) { Serial1.write(c); }
|
|
#endif
|
|
#if defined(sdcardsupport)
|
|
inline void SDwrite (char c) { SDpfile.write(uint8_t(c)); } // Fix for RP2040
|
|
#endif
|
|
#if defined(ULISP_WIFI)
|
|
inline void WiFiwrite (char c) { client.write(c); }
|
|
#endif
|
|
#if defined(gfxsupport)
|
|
inline void gfxwrite (char c) { tft.write(c); }
|
|
#endif
|
|
|
|
pfun_t pstreamfun (object *args) {
|
|
int streamtype = SERIALSTREAM;
|
|
int address = 0;
|
|
pfun_t pfun = pserial;
|
|
if (args != NULL && first(args) != NULL) {
|
|
int stream = isstream(first(args));
|
|
streamtype = stream>>8; address = stream & 0xFF;
|
|
}
|
|
if (streamtype == I2CSTREAM) {
|
|
if (address < 128) pfun = i2cwrite;
|
|
#if defined(ULISP_I2C1)
|
|
else pfun = i2c1write;
|
|
#endif
|
|
} else if (streamtype == SPISTREAM) {
|
|
if (address < 128) pfun = spiwrite;
|
|
#if defined(ULISP_SPI1)
|
|
else pfun = spi1write;
|
|
#endif
|
|
} else if (streamtype == SERIALSTREAM) {
|
|
if (address == 0) pfun = pserial;
|
|
#if defined(ULISP_SERIAL3)
|
|
else if (address == 1) pfun = serial1write;
|
|
else if (address == 2) pfun = serial2write;
|
|
else if (address == 3) pfun = serial3write;
|
|
#elif defined(ULISP_SERIAL2)
|
|
else if (address == 1) pfun = serial1write;
|
|
else if (address == 2) pfun = serial2write;
|
|
#elif defined(ULISP_SERIAL1)
|
|
else if (address == 1) pfun = serial1write;
|
|
#endif
|
|
}
|
|
else if (streamtype == STRINGSTREAM) {
|
|
pfun = pstr;
|
|
}
|
|
#if defined(sdcardsupport)
|
|
else if (streamtype == SDSTREAM) pfun = (pfun_t)SDwrite;
|
|
#endif
|
|
#if defined(gfxsupport)
|
|
else if (streamtype == GFXSTREAM) pfun = (pfun_t)gfxwrite;
|
|
#endif
|
|
#if defined(ULISP_WIFI)
|
|
else if (streamtype == WIFISTREAM) pfun = (pfun_t)WiFiwrite;
|
|
#endif
|
|
else error2("unknown stream type");
|
|
return pfun;
|
|
}
|
|
|
|
// Check pins - these are board-specific not processor-specific
|
|
|
|
void checkanalogread (int pin) {
|
|
#if defined(ARDUINO_SAM_DUE)
|
|
if (!(pin>=54 && pin<=65)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_SAMD_ZERO)
|
|
if (!(pin>=14 && pin<=19)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_SAMD_MKRZERO)
|
|
if (!(pin>=15 && pin<=21)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_ITSYBITSY_M0)
|
|
if (!(pin>=14 && pin<=25)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_NEOTRINKEY_M0)
|
|
if (!(pin==1 || pin==2 || pin==6)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_GEMMA_M0)
|
|
if (!(pin>=8 && pin<=10)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_QTPY_M0)
|
|
if (!((pin>=0 && pin<=3) || (pin>=6 && pin<=10))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_SEEED_XIAO_M0)
|
|
if (!(pin>=0 && pin<=10)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_METRO_M4)
|
|
if (!(pin>=14 && pin<=21)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_ITSYBITSY_M4) || defined(ARDUINO_FEATHER_M4)
|
|
if (!(pin>=14 && pin<=20)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_PYBADGE_M4)
|
|
if (!(pin>=14 && pin<=23)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_PYGAMER_M4)
|
|
if (!(pin>=14 && pin<=25)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_WIO_TERMINAL)
|
|
if (!((pin>=0 && pin<=8))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_GRAND_CENTRAL_M4)
|
|
if (!((pin>=67 && pin<=74) || (pin>=54 && pin<=61))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_BBC_MICROBIT) || defined(ARDUINO_SINOBIT)
|
|
if (!((pin>=0 && pin<=4) || pin==10)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_BBC_MICROBIT_V2)
|
|
if (!((pin>=0 && pin<=4) || pin==10 || pin==29)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_CALLIOPE_MINI)
|
|
if (!(pin==1 || pin==2 || (pin>=4 && pin<=6) || pin==21)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_NRF52840_ITSYBITSY)
|
|
if (!(pin>=14 && pin<=20)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_Seeed_XIAO_nRF52840) || defined(ARDUINO_Seeed_XIAO_nRF52840_Sense)
|
|
if (!(pin>=0 && pin<=5)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_NRF52840_CLUE)
|
|
if (!((pin>=0 && pin<=4) || pin==10 || pin==12 || pin==16)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_NRF52840_CIRCUITPLAY)
|
|
if (!(pin==0 || (pin>=2 && pin<=3) || pin==6 || (pin>=9 && pin<=10) || (pin>=22 && pin<=23))) error(invalidpin, number(pin));
|
|
#elif defined(MAX32620)
|
|
if (!(pin>=49 && pin<=52)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_TEENSY40)
|
|
if (!((pin>=14 && pin<=27))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_TEENSY41)
|
|
if (!((pin>=14 && pin<=27) || (pin>=38 && pin<=41))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \
|
|
|| defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) \
|
|
|| defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2040) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) \
|
|
|| defined(ARDUINO_PIMORONI_PICO_PLUS_2)
|
|
if (!(pin>=26 && pin<=29)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_MINIMA) || defined(ARDUINO_UNOWIFIR4)
|
|
if (!((pin>=14 && pin<=21))) error(invalidpin, number(pin));
|
|
#endif
|
|
}
|
|
|
|
void checkanalogwrite (int pin) {
|
|
#if defined(ARDUINO_SAM_DUE)
|
|
if (!((pin>=2 && pin<=13) || pin==66 || pin==67)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_SAMD_ZERO)
|
|
if (!((pin>=3 && pin<=6) || (pin>=8 && pin<=13) || pin==14)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_SAMD_MKRZERO)
|
|
if (!((pin>=0 && pin<=8) || pin==10 || pin==18 || pin==19)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_ITSYBITSY_M0)
|
|
if (!((pin>=3 && pin<=6) || (pin>=8 && pin<=13) || (pin>=15 && pin<=16) || (pin>=22 && pin<=25))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_NEOTRINKEY_M0)
|
|
error2("not supported");
|
|
#elif defined(ARDUINO_GEMMA_M0)
|
|
if (!(pin==0 || pin==2 || pin==9 || pin==10)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_QTPY_M0)
|
|
if (!(pin==0 || (pin>=2 && pin<=10))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_SEEED_XIAO_M0)
|
|
if (!(pin>=0 && pin<=10)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_METRO_M4)
|
|
if (!(pin>=0 && pin<=15)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_ITSYBITSY_M4)
|
|
if (!(pin==0 || pin==1 || pin==4 || pin==5 || pin==7 || (pin>=9 && pin<=15) || pin==21 || pin==22)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_FEATHER_M4)
|
|
if (!(pin==0 || pin==1 || (pin>=4 && pin<=6) || (pin>=9 && pin<=13) || pin==14 || pin==15 || pin==17 || pin==21 || pin==22)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_PYBADGE_M4)
|
|
if (!(pin==4 || pin==7 || pin==9 || (pin>=12 && pin<=13) || (pin>=24 && pin<=25) || (pin>=46 && pin<=47))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_PYGAMER_M4)
|
|
if (!(pin==4 || pin==7 || pin==9 || (pin>=12 && pin<=13) || (pin>=26 && pin<=27) || (pin>=46 && pin<=47))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_WIO_TERMINAL)
|
|
if (!((pin>=0 && pin<=2) || pin==6 || pin==8 || (pin>=12 && pin<=20) || pin==24)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_GRAND_CENTRAL_M4)
|
|
if (!((pin>=2 && pin<=9) || pin==11 || (pin>=13 && pin<=45) || pin==48 || (pin>=50 && pin<=53) || pin==58 || pin==61 || pin==68 || pin==69)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_BBC_MICROBIT) || defined(ARDUINO_BBC_MICROBIT_V2) || defined(ARDUINO_SINOBIT)
|
|
if (!(pin>=0 && pin<=32)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_CALLIOPE_MINI)
|
|
if (!(pin>=0 && pin<=30)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_NRF52840_ITSYBITSY)
|
|
if (!(pin>=0 && pin<=25)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_NRF52840_CLUE)
|
|
if (!(pin>=0 && pin<=46)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_NRF52840_CIRCUITPLAY)
|
|
if (!(pin>=0 && pin<=35)) error(invalidpin, number(pin));
|
|
#elif defined(MAX32620)
|
|
if (!((pin>=20 && pin<=29) || pin==32 || (pin>=40 && pin<=48))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_TEENSY40)
|
|
if (!((pin>=0 && pin<=15) || (pin>=18 && pin<=19) || (pin>=22 && pin<=25) || (pin>=28 && pin<=29) || (pin>=33 && pin<=39))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_TEENSY41)
|
|
if (!((pin>=0 && pin<=15) || (pin>=18 && pin<=19) || (pin>=22 && pin<=25) || (pin>=28 && pin<=29) || pin==33 || (pin>=36 && pin<=37))) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) \
|
|
|| defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) \
|
|
|| defined(ARDUINO_SEEED_XIAO_RP2040) || defined(ARDUINO_RASPBERRY_PI_PICO_2) \
|
|
|| defined(ARDUINO_PIMORONI_PICO_PLUS_2)
|
|
if (!(pin>=0 && pin<=29)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_RASPBERRY_PI_PICO_W) || defined(ARDUINO_RASPBERRY_PI_PICO_2W)
|
|
if (!((pin>=0 && pin<=29) || pin == 32)) error(invalidpin, number(pin));
|
|
#elif defined(ARDUINO_MINIMA) || defined(ARDUINO_UNOWIFIR4)
|
|
if (!((pin>=0 && pin<=21))) error(invalidpin, number(pin));
|
|
#endif
|
|
}
|
|
|
|
// Note
|
|
|
|
const int scale[] = {4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902};
|
|
|
|
void playnote (int pin, int note, int octave) {
|
|
#if defined(ARDUINO_NRF52840_CLUE) || defined(ARDUINO_NRF52840_CIRCUITPLAY) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \
|
|
|| defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) \
|
|
|| defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2040) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) \
|
|
|| defined(ARDUINO_PIMORONI_PICO_PLUS_2) || defined(ARDUINO_WIO_TERMINAL)
|
|
int oct = octave + note/12;
|
|
int prescaler = 8 - oct;
|
|
if (prescaler<0 || prescaler>8) error("octave out of range", number(oct));
|
|
tone(pin, scale[note%12]>>prescaler);
|
|
#else
|
|
(void) pin, (void) note, (void) octave;
|
|
#endif
|
|
}
|
|
|
|
void nonote (int pin) {
|
|
#if defined(ARDUINO_NRF52840_CLUE) || defined(ARDUINO_NRF52840_CIRCUITPLAY) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \
|
|
|| defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) \
|
|
|| defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2040) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) \
|
|
|| defined(ARDUINO_PIMORONI_PICO_PLUS_2) || defined(ARDUINO_WIO_TERMINAL)
|
|
noTone(pin);
|
|
#else
|
|
(void) pin;
|
|
#endif
|
|
}
|
|
|
|
// Sleep
|
|
|
|
#if defined(CPU_ATSAMD21)
|
|
void WDT_Handler(void) {
|
|
// ISR for watchdog early warning
|
|
WDT->CTRL.bit.ENABLE = 0; // Disable watchdog
|
|
while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
|
|
WDT->INTFLAG.bit.EW = 1; // Clear interrupt flag
|
|
}
|
|
#endif
|
|
|
|
void initsleep () {
|
|
#if defined(CPU_ATSAMD21)
|
|
// One-time initialization of watchdog timer.
|
|
|
|
// Generic clock generator 2, divisor = 32 (2^(DIV+1))
|
|
GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(4);
|
|
// Enable clock generator 2 using low-power 32KHz oscillator.
|
|
// With /32 divisor above, this yields 1024Hz clock.
|
|
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) |
|
|
GCLK_GENCTRL_GENEN |
|
|
GCLK_GENCTRL_SRC_OSCULP32K |
|
|
GCLK_GENCTRL_DIVSEL;
|
|
while(GCLK->STATUS.bit.SYNCBUSY);
|
|
// WDT clock = clock gen 2
|
|
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT |
|
|
GCLK_CLKCTRL_CLKEN |
|
|
GCLK_CLKCTRL_GEN_GCLK2;
|
|
|
|
// Enable WDT early-warning interrupt
|
|
NVIC_DisableIRQ(WDT_IRQn);
|
|
NVIC_ClearPendingIRQ(WDT_IRQn);
|
|
NVIC_SetPriority(WDT_IRQn, 0); // Top priority
|
|
NVIC_EnableIRQ(WDT_IRQn);
|
|
#endif
|
|
}
|
|
|
|
void doze (int secs) {
|
|
#if defined(CPU_ATSAMD21)
|
|
WDT->CTRL.reg = 0; // Disable watchdog for config
|
|
while(WDT->STATUS.bit.SYNCBUSY);
|
|
WDT->INTENSET.bit.EW = 1; // Enable early warning interrupt
|
|
WDT->CONFIG.bit.PER = 0xB; // Period = max
|
|
WDT->CONFIG.bit.WINDOW = 0x7; // Set time of interrupt = 1024 cycles = 1 sec
|
|
WDT->CTRL.bit.WEN = 1; // Enable window mode
|
|
while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
|
|
|
|
SysTick->CTRL = 0; // Stop SysTick interrupts
|
|
|
|
while (secs > 0) {
|
|
WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;// Clear watchdog interval
|
|
while(WDT->STATUS.bit.SYNCBUSY);
|
|
WDT->CTRL.bit.ENABLE = 1; // Start watchdog now!
|
|
while(WDT->STATUS.bit.SYNCBUSY);
|
|
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // Deepest sleep
|
|
__DSB();
|
|
__WFI(); // Wait for interrupt
|
|
secs--;
|
|
}
|
|
SysTick->CTRL = 7; // Restart SysTick interrupts
|
|
#else
|
|
delay(1000*secs);
|
|
#endif
|
|
}
|
|
|
|
// Prettyprint
|
|
|
|
const int PPINDENT = 2;
|
|
const int PPWIDTH = 80;
|
|
const int GFXPPWIDTH = 52; // 320 pixel wide screen
|
|
int ppwidth = PPWIDTH;
|
|
|
|
void pcount (char c) {
|
|
if (c == '\n') PrintCount++;
|
|
PrintCount++;
|
|
}
|
|
|
|
/*
|
|
atomwidth - calculates the character width of an atom
|
|
*/
|
|
uint8_t atomwidth (object *obj) {
|
|
PrintCount = 0;
|
|
printobject(obj, pcount);
|
|
return PrintCount;
|
|
}
|
|
|
|
/*
|
|
basewidth - calculates the character width of an integer printed in a given base
|
|
*/
|
|
uint8_t basewidth (object *obj, uint8_t base) {
|
|
PrintCount = 0;
|
|
pintbase(obj->integer, base, pcount);
|
|
return PrintCount;
|
|
}
|
|
|
|
/*
|
|
quoted - tests whether an object is quoted
|
|
*/
|
|
bool quoted (object *obj) {
|
|
return (consp(obj) && car(obj) != NULL && car(obj)->name == sym(QUOTE) && consp(cdr(obj)) && cddr(obj) == NULL);
|
|
}
|
|
|
|
/*
|
|
subwidth - returns the space left from w after printing object
|
|
*/
|
|
int subwidth (object *obj, int w) {
|
|
if (atom(obj)) return w - atomwidth(obj);
|
|
if (quoted(obj)) obj = car(cdr(obj));
|
|
return subwidthlist(obj, w - 1);
|
|
}
|
|
|
|
/*
|
|
subwidth - returns the space left from w after printing a list
|
|
*/
|
|
int subwidthlist (object *form, int w) {
|
|
while (form != NULL && w >= 0) {
|
|
if (atom(form)) return w - (2 + atomwidth(form));
|
|
w = subwidth(car(form), w - 1);
|
|
form = cdr(form);
|
|
}
|
|
return w;
|
|
}
|
|
|
|
/*
|
|
superprint - handles pretty-printing
|
|
*/
|
|
void superprint (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('\'');
|
|
superprint(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(" . ", 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); }
|
|
superprint(car(form), lm+extra, pfun);
|
|
form = cdr(form);
|
|
}
|
|
pfun(')');
|
|
}
|
|
}
|
|
|
|
/*
|
|
edit - the Lisp tree editor
|
|
Steps through a function definition, editing it a bit at a time, using single-key editing commands.
|
|
*/
|
|
object *edit (object *fun) {
|
|
while (1) {
|
|
if (tstflag(EXITEDITOR)) return fun;
|
|
char c = gserial();
|
|
if (c == 'q') setflag(EXITEDITOR);
|
|
else if (c == 'b') return fun;
|
|
else if (c == 'r') fun = read(gserial);
|
|
else if (c == '\n') { pfl(pserial); superprint(fun, 0, pserial); pln(pserial); }
|
|
else if (c == 'c') fun = cons(read(gserial), fun);
|
|
else if (atom(fun)) pserial('!');
|
|
else if (c == 'd') fun = cons(car(fun), edit(cdr(fun)));
|
|
else if (c == 'a') fun = cons(edit(car(fun)), cdr(fun));
|
|
else if (c == 'x') fun = cdr(fun);
|
|
else pserial('?');
|
|
}
|
|
}
|
|
|
|
// Assembler
|
|
|
|
object *call (int entry, int nargs, object *args, object *env) {
|
|
#if defined(CODESIZE)
|
|
(void) env;
|
|
int param[4];
|
|
for (int i=0; i<nargs; i++) {
|
|
object *arg = first(args);
|
|
if (integerp(arg)) param[i] = arg->integer;
|
|
else param[i] = (uintptr_t)arg;
|
|
args = cdr(args);
|
|
}
|
|
int w = ((intfn_ptr_type)&MyCode[entry])(param[0], param[1], param[2], param[3]);
|
|
return number(w);
|
|
#else
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
void putcode (object *arg, int origin, int pc) {
|
|
#if defined(CODESIZE)
|
|
int code = checkinteger(arg);
|
|
MyCode[origin+pc] = code & 0xff;
|
|
MyCode[origin+pc+1] = (code>>8) & 0xff;
|
|
#if defined(assemblerlist)
|
|
printhex4(pc, pserial);
|
|
printhex4(code, pserial);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
int assemble (int pass, int origin, object *entries, object *env, object *pcpair) {
|
|
int pc = 0; cdr(pcpair) = number(pc);
|
|
while (entries != NULL) {
|
|
object *arg = first(entries);
|
|
if (symbolp(arg)) {
|
|
if (pass == 2) {
|
|
#if defined(assemblerlist)
|
|
printhex4(pc, pserial);
|
|
indent(5, ' ', pserial);
|
|
printobject(arg, pserial); pln(pserial);
|
|
#endif
|
|
} else {
|
|
object *pair = findvalue(arg, env);
|
|
cdr(pair) = number(pc);
|
|
}
|
|
} else {
|
|
object *argval = eval(arg, env);
|
|
if (listp(argval)) {
|
|
object *arglist = argval;
|
|
while (arglist != NULL) {
|
|
if (pass == 2) {
|
|
putcode(first(arglist), origin, pc);
|
|
#if defined(assemblerlist)
|
|
if (arglist == argval) superprint(arg, 0, pserial);
|
|
pln(pserial);
|
|
#endif
|
|
}
|
|
pc = pc + 2;
|
|
cdr(pcpair) = number(pc);
|
|
arglist = cdr(arglist);
|
|
}
|
|
} else if (integerp(argval)) {
|
|
if (pass == 2) {
|
|
putcode(argval, origin, pc);
|
|
#if defined(assemblerlist)
|
|
superprint(arg, 0, pserial); pln(pserial);
|
|
#endif
|
|
}
|
|
pc = pc + 2;
|
|
cdr(pcpair) = number(pc);
|
|
} else error("illegal entry", arg);
|
|
}
|
|
entries = cdr(entries);
|
|
}
|
|
// Round up to multiple of 4 to give code size
|
|
if (pc%4 != 0) pc = pc + 4 - pc%4;
|
|
return pc;
|
|
}
|
|
|
|
// Special forms
|
|
|
|
object *sp_quote (object *args, object *env) {
|
|
(void) env;
|
|
return first(args);
|
|
}
|
|
|
|
/*
|
|
(or item*)
|
|
Evaluates its arguments until one returns non-nil, and returns its value.
|
|
*/
|
|
object *sp_or (object *args, object *env) {
|
|
while (args != NULL) {
|
|
object *val = eval(car(args), env);
|
|
if (val != NULL) return val;
|
|
args = cdr(args);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(defun name (parameters) form*)
|
|
Defines a function.
|
|
*/
|
|
object *sp_defun (object *args, object *env) {
|
|
(void) env;
|
|
object *var = first(args);
|
|
if (!symbolp(var)) error(notasymbol, var);
|
|
object *val = cons(bsymbol(LAMBDA), cdr(args));
|
|
object *pair = value(var->name, GlobalEnv);
|
|
if (pair != NULL) cdr(pair) = val;
|
|
else push(cons(var, val), GlobalEnv);
|
|
return var;
|
|
}
|
|
|
|
/*
|
|
(defvar variable form)
|
|
Defines a global variable.
|
|
*/
|
|
object *sp_defvar (object *args, object *env) {
|
|
object *var = first(args);
|
|
if (!symbolp(var)) error(notasymbol, var);
|
|
object *val = NULL;
|
|
args = cdr(args);
|
|
if (args != NULL) { setflag(NOESC); val = eval(first(args), env); clrflag(NOESC); }
|
|
object *pair = value(var->name, GlobalEnv);
|
|
if (pair != NULL) cdr(pair) = val;
|
|
else push(cons(var, val), GlobalEnv);
|
|
return var;
|
|
}
|
|
|
|
/*
|
|
(setq symbol value [symbol value]*)
|
|
For each pair of arguments assigns the value of the second argument
|
|
to the variable specified in the first argument.
|
|
*/
|
|
object *sp_setq (object *args, object *env) {
|
|
object *arg = nil; builtin_t setq = Context;
|
|
while (args != NULL) {
|
|
if (cdr(args) == NULL) { Context = setq; error2(oddargs); }
|
|
object *pair = findvalue(first(args), env);
|
|
arg = eval(second(args), env);
|
|
cdr(pair) = arg;
|
|
args = cddr(args);
|
|
}
|
|
return arg;
|
|
}
|
|
|
|
/*
|
|
(loop forms*)
|
|
Executes its arguments repeatedly until one of the arguments calls (return),
|
|
which then causes an exit from the loop.
|
|
*/
|
|
object *sp_loop (object *args, object *env) {
|
|
object *start = args;
|
|
for (;;) {
|
|
args = start;
|
|
while (args != NULL) {
|
|
object *result = eval(car(args),env);
|
|
if (tstflag(RETURNFLAG)) {
|
|
clrflag(RETURNFLAG);
|
|
return result;
|
|
}
|
|
args = cdr(args);
|
|
}
|
|
testescape();
|
|
}
|
|
}
|
|
|
|
/*
|
|
(push item place)
|
|
Modifies the value of place, which should be a list, to add item onto the front of the list,
|
|
and returns the new list.
|
|
*/
|
|
object *sp_push (object *args, object *env) {
|
|
int bit;
|
|
object *item = eval(first(args), env);
|
|
object **loc = place(second(args), env, &bit);
|
|
if (bit != -1) error2(invalidarg);
|
|
push(item, *loc);
|
|
return *loc;
|
|
}
|
|
|
|
/*
|
|
(pop place)
|
|
Modifies the value of place, which should be a non-nil list, to remove its first item,
|
|
and returns that item.
|
|
*/
|
|
object *sp_pop (object *args, object *env) {
|
|
int bit;
|
|
object *arg = first(args);
|
|
if (arg == NULL) error2(invalidarg);
|
|
object **loc = place(arg, env, &bit);
|
|
if (bit < -1) error(invalidarg, arg);
|
|
if (!consp(*loc)) error(notalist, *loc);
|
|
object *result = car(*loc);
|
|
pop(*loc);
|
|
return result;
|
|
}
|
|
|
|
// Accessors
|
|
|
|
/*
|
|
(incf place [number])
|
|
Increments a place, which should have an numeric value, and returns the result.
|
|
The third argument is an optional increment which defaults to 1.
|
|
*/
|
|
object *sp_incf (object *args, object *env) {
|
|
int bit;
|
|
object **loc = place(first(args), env, &bit);
|
|
if (bit < -1) error2(notanumber);
|
|
args = cdr(args);
|
|
|
|
object *x = *loc;
|
|
object *inc = (args != NULL) ? eval(first(args), env) : NULL;
|
|
|
|
if (bit != -1) {
|
|
int increment;
|
|
if (inc == NULL) increment = 1; else increment = checkbitvalue(inc);
|
|
int newvalue = (((*loc)->integer)>>bit & 1) + increment;
|
|
|
|
if (newvalue & ~1) error2("result is not a bit value");
|
|
*loc = number((((*loc)->integer) & ~(1<<bit)) | newvalue<<bit);
|
|
return number(newvalue);
|
|
}
|
|
|
|
if (floatp(x) || floatp(inc)) {
|
|
float increment;
|
|
float value = checkintfloat(x);
|
|
|
|
if (inc == NULL) increment = 1.0; else increment = checkintfloat(inc);
|
|
|
|
*loc = makefloat(value + increment);
|
|
} else if (integerp(x) && (integerp(inc) || inc == NULL)) {
|
|
int increment;
|
|
int value = x->integer;
|
|
|
|
if (inc == NULL) increment = 1; else increment = inc->integer;
|
|
|
|
if (increment < 1) {
|
|
if (INT_MIN - increment > value) *loc = makefloat((float)value + (float)increment);
|
|
else *loc = number(value + increment);
|
|
} else {
|
|
if (INT_MAX - increment < value) *loc = makefloat((float)value + (float)increment);
|
|
else *loc = number(value + increment);
|
|
}
|
|
} else error2(notanumber);
|
|
return *loc;
|
|
}
|
|
|
|
/*
|
|
(decf place [number])
|
|
Decrements a place, which should have an numeric value, and returns the result.
|
|
The third argument is an optional decrement which defaults to 1.
|
|
*/
|
|
object *sp_decf (object *args, object *env) {
|
|
int bit;
|
|
object **loc = place(first(args), env, &bit);
|
|
if (bit < -1) error2(notanumber);
|
|
args = cdr(args);
|
|
|
|
object *x = *loc;
|
|
object *dec = (args != NULL) ? eval(first(args), env) : NULL;
|
|
|
|
if (bit != -1) {
|
|
int decrement;
|
|
if (dec == NULL) decrement = 1; else decrement = checkbitvalue(dec);
|
|
int newvalue = (((*loc)->integer)>>bit & 1) - decrement;
|
|
|
|
if (newvalue & ~1) error2("result is not a bit value");
|
|
*loc = number((((*loc)->integer) & ~(1<<bit)) | newvalue<<bit);
|
|
return number(newvalue);
|
|
}
|
|
|
|
if (floatp(x) || floatp(dec)) {
|
|
float decrement;
|
|
float value = checkintfloat(x);
|
|
|
|
if (dec == NULL) decrement = 1.0; else decrement = checkintfloat(dec);
|
|
|
|
*loc = makefloat(value - decrement);
|
|
} else if (integerp(x) && (integerp(dec) || dec == NULL)) {
|
|
int decrement;
|
|
int value = x->integer;
|
|
|
|
if (dec == NULL) decrement = 1; else decrement = dec->integer;
|
|
|
|
if (decrement < 1) {
|
|
if (INT_MAX + decrement < value) *loc = makefloat((float)value - (float)decrement);
|
|
else *loc = number(value - decrement);
|
|
} else {
|
|
if (INT_MIN + decrement > value) *loc = makefloat((float)value - (float)decrement);
|
|
else *loc = number(value - decrement);
|
|
}
|
|
} else error2(notanumber);
|
|
return *loc;
|
|
}
|
|
|
|
/*
|
|
(setf place value [place value]*)
|
|
For each pair of arguments modifies a place to the result of evaluating value.
|
|
*/
|
|
object *sp_setf (object *args, object *env) {
|
|
int bit; builtin_t setf = Context;
|
|
object *arg = nil;
|
|
while (args != NULL) {
|
|
if (cdr(args) == NULL) { Context = setf; error2(oddargs); }
|
|
object **loc = place(first(args), env, &bit);
|
|
arg = eval(second(args), env);
|
|
if (bit == -1) *loc = arg;
|
|
else if (bit < -1) (*loc)->chars = ((*loc)->chars & ~(0xff<<((-bit-2)<<3))) | checkchar(arg)<<((-bit-2)<<3);
|
|
else *loc = number((checkinteger(*loc) & ~(1<<bit)) | checkbitvalue(arg)<<bit);
|
|
args = cddr(args);
|
|
}
|
|
return arg;
|
|
}
|
|
|
|
// Other special forms
|
|
|
|
/*
|
|
(dolist (var list [result]) form*)
|
|
Sets the local variable var to each element of list in turn, and executes the forms.
|
|
It then returns result, or nil if result is omitted.
|
|
*/
|
|
object *sp_dolist (object *args, object *env) {
|
|
object *params = checkarguments(args, 2, 3);
|
|
object *var = first(params);
|
|
object *list = eval(second(params), env);
|
|
protect(list); // Don't GC the list
|
|
object *pair = cons(var,nil);
|
|
push(pair,env);
|
|
params = cddr(params);
|
|
args = cdr(args);
|
|
while (list != NULL) {
|
|
if (improperp(list)) error(notproper, list);
|
|
cdr(pair) = first(list);
|
|
object *forms = args;
|
|
while (forms != NULL) {
|
|
object *result = eval(car(forms), env);
|
|
if (tstflag(RETURNFLAG)) {
|
|
clrflag(RETURNFLAG);
|
|
unprotect();
|
|
return result;
|
|
}
|
|
forms = cdr(forms);
|
|
}
|
|
list = cdr(list);
|
|
}
|
|
cdr(pair) = nil;
|
|
unprotect();
|
|
if (params == NULL) return nil;
|
|
return eval(car(params), env);
|
|
}
|
|
|
|
/*
|
|
(dotimes (var number [result]) form*)
|
|
Executes the forms number times, with the local variable var set to each integer from 0 to number-1 in turn.
|
|
It then returns result, or nil if result is omitted.
|
|
*/
|
|
object *sp_dotimes (object *args, object *env) {
|
|
object *params = checkarguments(args, 2, 3);
|
|
object *var = first(params);
|
|
int count = checkinteger(eval(second(params), env));
|
|
int index = 0;
|
|
params = cddr(params);
|
|
object *pair = cons(var,number(0));
|
|
push(pair,env);
|
|
args = cdr(args);
|
|
while (index < count) {
|
|
cdr(pair) = number(index);
|
|
object *forms = args;
|
|
while (forms != NULL) {
|
|
object *result = eval(car(forms), env);
|
|
if (tstflag(RETURNFLAG)) {
|
|
clrflag(RETURNFLAG);
|
|
return result;
|
|
}
|
|
forms = cdr(forms);
|
|
}
|
|
index++;
|
|
}
|
|
cdr(pair) = number(index);
|
|
if (params == NULL) return nil;
|
|
return eval(car(params), env);
|
|
}
|
|
|
|
/*
|
|
(do ((var [init [step]])*) (end-test result*) form*)
|
|
Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step sequentially.
|
|
The forms are executed until end-test is true. It returns result.
|
|
*/
|
|
object *sp_do (object *args, object *env) {
|
|
return dobody(args, env, false);
|
|
}
|
|
|
|
/*
|
|
(do* ((var [init [step]])*) (end-test result*) form*)
|
|
Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step in parallel.
|
|
The forms are executed until end-test is true. It returns result.
|
|
*/
|
|
object *sp_dostar (object *args, object *env) {
|
|
return dobody(args, env, true);
|
|
}
|
|
|
|
/*
|
|
(trace [function]*)
|
|
Turns on tracing of up to TRACEMAX user-defined functions,
|
|
and returns a list of the functions currently being traced.
|
|
*/
|
|
object *sp_trace (object *args, object *env) {
|
|
(void) env;
|
|
while (args != NULL) {
|
|
object *var = first(args);
|
|
if (!symbolp(var)) error(notasymbol, var);
|
|
trace(var->name);
|
|
args = cdr(args);
|
|
}
|
|
int i = 0;
|
|
while (i < TRACEMAX) {
|
|
if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args);
|
|
i++;
|
|
}
|
|
return args;
|
|
}
|
|
|
|
/*
|
|
(untrace [function]*)
|
|
Turns off tracing of up to TRACEMAX user-defined functions, and returns a list of the functions untraced.
|
|
If no functions are specified it untraces all functions.
|
|
*/
|
|
object *sp_untrace (object *args, object *env) {
|
|
(void) env;
|
|
if (args == NULL) {
|
|
int i = 0;
|
|
while (i < TRACEMAX) {
|
|
if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args);
|
|
TraceFn[i] = 0;
|
|
i++;
|
|
}
|
|
} else {
|
|
while (args != NULL) {
|
|
object *var = first(args);
|
|
if (!symbolp(var)) error(notasymbol, var);
|
|
untrace(var->name);
|
|
args = cdr(args);
|
|
}
|
|
}
|
|
return args;
|
|
}
|
|
|
|
/*
|
|
(for-millis ([number]) form*)
|
|
Executes the forms and then waits until a total of number milliseconds have elapsed.
|
|
Returns the total number of milliseconds taken.
|
|
*/
|
|
object *sp_formillis (object *args, object *env) {
|
|
object *param = checkarguments(args, 0, 1);
|
|
unsigned long start = millis();
|
|
unsigned long now, total = 0;
|
|
if (param != NULL) total = checkinteger(eval(first(param), env));
|
|
eval(tf_progn(cdr(args),env), env);
|
|
do {
|
|
now = millis() - start;
|
|
testescape();
|
|
} while (now < total);
|
|
if (now <= INT_MAX) return number(now);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(time form)
|
|
Prints the value returned by the form, and the time taken to evaluate the form
|
|
in milliseconds or seconds.
|
|
*/
|
|
object *sp_time (object *args, object *env) {
|
|
unsigned long start = millis();
|
|
object *result = eval(first(args), env);
|
|
unsigned long elapsed = millis() - start;
|
|
printobject(result, pserial);
|
|
pfstring("\nTime: ", pserial);
|
|
if (elapsed < 1000) {
|
|
pint(elapsed, pserial);
|
|
pfstring(" ms\n", pserial);
|
|
} else {
|
|
elapsed = elapsed+50;
|
|
pint(elapsed/1000, pserial);
|
|
pserial('.'); pint((elapsed/100)%10, pserial);
|
|
pfstring(" s\n", pserial);
|
|
}
|
|
return bsymbol(NOTHING);
|
|
}
|
|
|
|
/*
|
|
(with-output-to-string (str) form*)
|
|
Returns a string containing the output to the stream variable str.
|
|
*/
|
|
object *sp_withoutputtostring (object *args, object *env) {
|
|
object *params = checkarguments(args, 1, 1);
|
|
object *var = first(params);
|
|
object *pair = cons(var, stream(STRINGSTREAM, 0));
|
|
push(pair,env);
|
|
object *string = startstring();
|
|
protect(string);
|
|
object *forms = cdr(args);
|
|
eval(tf_progn(forms,env), env);
|
|
unprotect();
|
|
return string;
|
|
}
|
|
|
|
/*
|
|
(with-serial (str port [baud]) form*)
|
|
Evaluates the forms with str bound to a serial-stream using port.
|
|
The optional baud gives the baud rate divided by 100, default 96.
|
|
*/
|
|
object *sp_withserial (object *args, object *env) {
|
|
object *params = checkarguments(args, 2, 3);
|
|
object *var = first(params);
|
|
int address = checkinteger(eval(second(params), env));
|
|
params = cddr(params);
|
|
int baud = BAUDRATE;
|
|
if (params != NULL) baud = checkinteger(eval(first(params), env));
|
|
object *pair = cons(var, stream(SERIALSTREAM, address));
|
|
push(pair,env);
|
|
serialbegin(address, baud);
|
|
object *forms = cdr(args);
|
|
object *result = eval(tf_progn(forms,env), env);
|
|
serialend(address);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
(with-i2c (str [port] address [read-p]) form*)
|
|
Evaluates the forms with str bound to an i2c-stream defined by address.
|
|
If read-p is nil or omitted the stream is written to, otherwise it specifies the number of bytes
|
|
to be read from the stream. If port is omitted it defaults to 0, otherwise it specifies the port, 0 or 1.
|
|
*/
|
|
object *sp_withi2c (object *args, object *env) {
|
|
object *params = checkarguments(args, 2, 4);
|
|
object *var = first(params);
|
|
int address = checkinteger(eval(second(params), env));
|
|
params = cddr(params);
|
|
if ((address == 0 || address == 1) && params != NULL) {
|
|
address = address * 128 + checkinteger(eval(first(params), env));
|
|
params = cdr(params);
|
|
}
|
|
int read = 0; // Write
|
|
I2Ccount = 0;
|
|
if (params != NULL) {
|
|
object *rw = eval(first(params), env);
|
|
if (integerp(rw)) I2Ccount = rw->integer;
|
|
read = (rw != NULL);
|
|
}
|
|
// Top bit of address is I2C port
|
|
TwoWire *port = &Wire;
|
|
#if defined(ULISP_I2C1)
|
|
if (address > 127) port = &Wire1;
|
|
#endif
|
|
I2Cinit(port, 1); // Pullups
|
|
object *pair = cons(var, (I2Cstart(port, address & 0x7F, read)) ? stream(I2CSTREAM, address) : nil);
|
|
push(pair,env);
|
|
object *forms = cdr(args);
|
|
object *result = eval(tf_progn(forms,env), env);
|
|
I2Cstop(port, read);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
(with-spi (str pin [clock] [bitorder] [mode] [port]) form*)
|
|
Evaluates the forms with str bound to an spi-stream.
|
|
The parameters specify the enable pin, clock in kHz (default 4000),
|
|
bitorder 0 for LSBFIRST and 1 for MSBFIRST (default 1), SPI mode (default 0), and port 0 or 1 (default 0).
|
|
*/
|
|
object *sp_withspi (object *args, object *env) {
|
|
object *params = checkarguments(args, 2, 6);
|
|
object *var = first(params);
|
|
params = cdr(params);
|
|
if (params == NULL) error2(nostream);
|
|
int pin = checkinteger(eval(car(params), env));
|
|
pinMode(pin, OUTPUT);
|
|
digitalWrite(pin, HIGH);
|
|
params = cdr(params);
|
|
int clock = 4000, mode = SPI_MODE0, address = 0; // Defaults
|
|
BitOrder bitorder = MSBFIRST;
|
|
if (params != NULL) {
|
|
clock = checkinteger(eval(car(params), env));
|
|
params = cdr(params);
|
|
if (params != NULL) {
|
|
bitorder = (checkinteger(eval(car(params), env)) == 0) ? LSBFIRST : MSBFIRST;
|
|
params = cdr(params);
|
|
if (params != NULL) {
|
|
int modeval = checkinteger(eval(car(params), env));
|
|
mode = (modeval == 3) ? SPI_MODE3 : (modeval == 2) ? SPI_MODE2 : (modeval == 1) ? SPI_MODE1 : SPI_MODE0;
|
|
params = cdr(params);
|
|
if (params != NULL) {
|
|
address = checkinteger(eval(car(params), env));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
object *pair = cons(var, stream(SPISTREAM, pin + 128*address));
|
|
push(pair,env);
|
|
SPIClass *spiClass = &SPI;
|
|
#if defined(ULISP_SPI1)
|
|
if (address == 1) spiClass = &SPI1;
|
|
#endif
|
|
spiClass->begin();
|
|
spiClass->beginTransaction(SPISettings(((unsigned long)clock * 1000), bitorder, mode));
|
|
digitalWrite(pin, LOW);
|
|
object *forms = cdr(args);
|
|
object *result = eval(tf_progn(forms,env), env);
|
|
digitalWrite(pin, HIGH);
|
|
spiClass->endTransaction();
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
(with-sd-card (str filename [mode]) form*)
|
|
Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename.
|
|
If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite.
|
|
*/
|
|
object *sp_withsdcard (object *args, object *env) {
|
|
#if defined(sdcardsupport)
|
|
object *params = checkarguments(args, 2, 3);
|
|
object *var = first(params);
|
|
params = cdr(params);
|
|
if (params == NULL) error2("no filename specified");
|
|
builtin_t temp = Context;
|
|
object *filename = eval(first(params), env);
|
|
Context = temp;
|
|
if (!stringp(filename)) error("filename is not a string", filename);
|
|
params = cdr(params);
|
|
#if defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_2W)
|
|
#if defined(PLATFORM_PICOCALC)
|
|
if(!SD.begin(SDCARD_SS_PIN,(uint32_t) SPI_HALF_SPEED, SPI )){
|
|
error2(PSTR("failed to init SD card"));
|
|
return nil;
|
|
}
|
|
#else
|
|
SD.begin(SDCARD_SS_PIN,(uint32_t) SPI_HALF_SPEED, SPI1);
|
|
#endif
|
|
#else
|
|
SD.begin(SDCARD_SS_PIN);
|
|
#endif
|
|
int mode = 0;
|
|
if (params != NULL && first(params) != NULL) mode = checkinteger(first(params));
|
|
int oflag = O_READ;
|
|
if (mode == 1) oflag = O_RDWR | O_CREAT | O_APPEND; else if (mode == 2) oflag = O_RDWR | O_CREAT | O_TRUNC;
|
|
if (mode >= 1) {
|
|
char buffer[BUFFERSIZE];
|
|
SDpfile = SD.open(MakeFilename(filename, buffer), oflag);
|
|
if (!SDpfile) error2("problem writing to SD card or invalid filename");
|
|
} else {
|
|
char buffer[BUFFERSIZE];
|
|
SDgfile = SD.open(MakeFilename(filename, buffer), oflag);
|
|
if (!SDgfile) error2("problem reading from SD card or invalid filename");
|
|
}
|
|
object *pair = cons(var, stream(SDSTREAM, 1));
|
|
push(pair,env);
|
|
object *forms = cdr(args);
|
|
object *result = eval(tf_progn(forms,env), env);
|
|
if (mode >= 1) SDpfile.close(); else SDgfile.close();
|
|
return result;
|
|
#else
|
|
(void) args, (void) env;
|
|
error2("not supported");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
// Assembler
|
|
|
|
/*
|
|
(defcode name (parameters) form*)
|
|
Creates a machine-code function called name from a series of 16-bit integers given in the body of the form.
|
|
These are written into RAM, and can be executed by calling the function in the same way as a normal Lisp function.
|
|
*/
|
|
object *sp_defcode (object *args, object *env) {
|
|
#if defined(CODESIZE)
|
|
setflag(NOESC);
|
|
object *var = first(args);
|
|
object *params = second(args);
|
|
if (!symbolp(var)) error("not a symbol", var);
|
|
|
|
// Make parameters into synonyms for registers r0, r1, etc
|
|
int regn = 0;
|
|
while (params != NULL) {
|
|
if (regn > 3) error("more than 4 parameters", var);
|
|
object *regpair = cons(car(params), bsymbol((builtin_t)((toradix40('r')*40+toradix40('0')+regn)*2560000))); // Symbol for r0 etc
|
|
push(regpair,env);
|
|
regn++;
|
|
params = cdr(params);
|
|
}
|
|
|
|
// Make *pc* a local variable for program counter
|
|
object *pcpair = cons(bsymbol(PSTAR), number(0));
|
|
push(pcpair,env);
|
|
|
|
args = cdr(args);
|
|
|
|
// Make labels into local variables
|
|
object *entries = cdr(args);
|
|
while (entries != NULL) {
|
|
object *arg = first(entries);
|
|
if (symbolp(arg)) {
|
|
object *pair = cons(arg,number(0));
|
|
push(pair,env);
|
|
}
|
|
entries = cdr(entries);
|
|
}
|
|
|
|
// First pass
|
|
int origin = 0;
|
|
int codesize = assemble(1, origin, cdr(args), env, pcpair);
|
|
|
|
// See if it will fit
|
|
object *globals = GlobalEnv;
|
|
while (globals != NULL) {
|
|
object *pair = car(globals);
|
|
if (pair != NULL && car(pair) != var && consp(cdr(pair))) { // Exclude me if I already exist
|
|
object *codeid = second(pair);
|
|
if (codeid->type == CODE) {
|
|
codesize = codesize + endblock(codeid) - startblock(codeid);
|
|
}
|
|
}
|
|
globals = cdr(globals);
|
|
}
|
|
if (codesize > CODESIZE) error("not enough room for code", var);
|
|
|
|
// Compact the code block, removing gaps
|
|
origin = 0;
|
|
object *block;
|
|
int smallest;
|
|
|
|
do {
|
|
smallest = CODESIZE;
|
|
globals = GlobalEnv;
|
|
while (globals != NULL) {
|
|
object *pair = car(globals);
|
|
if (pair != NULL && car(pair) != var && consp(cdr(pair))) { // Exclude me if I already exist
|
|
object *codeid = second(pair);
|
|
if (codeid->type == CODE) {
|
|
if (startblock(codeid) < smallest && startblock(codeid) >= origin) {
|
|
smallest = startblock(codeid);
|
|
block = codeid;
|
|
}
|
|
}
|
|
}
|
|
globals = cdr(globals);
|
|
}
|
|
|
|
// Compact fragmentation if necessary
|
|
if (smallest == origin) origin = endblock(block); // No gap
|
|
else if (smallest < CODESIZE) { // Slide block down
|
|
int target = origin;
|
|
for (int i=startblock(block); i<endblock(block); i++) {
|
|
MyCode[target] = MyCode[i];
|
|
target++;
|
|
}
|
|
block->integer = target<<16 | origin;
|
|
origin = target;
|
|
}
|
|
|
|
} while (smallest < CODESIZE);
|
|
|
|
// Second pass - origin is first free location
|
|
codesize = assemble(2, origin, cdr(args), env, pcpair);
|
|
|
|
object *val = cons(codehead((origin+codesize)<<16 | origin), args);
|
|
object *pair = value(var->name, GlobalEnv);
|
|
if (pair != NULL) cdr(pair) = val;
|
|
else push(cons(var, val), GlobalEnv);
|
|
clrflag(NOESC);
|
|
return var;
|
|
#else
|
|
error2("not available");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
// Tail-recursive forms
|
|
|
|
/*
|
|
(progn form*)
|
|
Evaluates several forms grouped together into a block, and returns the result of evaluating the last form.
|
|
*/
|
|
object *tf_progn (object *args, object *env) {
|
|
if (args == NULL) return nil;
|
|
object *more = cdr(args);
|
|
while (more != NULL) {
|
|
object *result = eval(car(args),env);
|
|
if (tstflag(RETURNFLAG)) return quote(result);
|
|
args = more;
|
|
more = cdr(args);
|
|
}
|
|
return car(args);
|
|
}
|
|
|
|
/*
|
|
(if test then [else])
|
|
Evaluates test. If it's non-nil the form then is evaluated and returned;
|
|
otherwise the form else is evaluated and returned.
|
|
*/
|
|
object *tf_if (object *args, object *env) {
|
|
if (args == NULL || cdr(args) == NULL) error2(toofewargs);
|
|
if (eval(first(args), env) != nil) return second(args);
|
|
args = cddr(args);
|
|
return (args != NULL) ? first(args) : nil;
|
|
}
|
|
|
|
/*
|
|
(cond ((test form*) (test form*) ... ))
|
|
Each argument is a list consisting of a test optionally followed by one or more forms.
|
|
If the test evaluates to non-nil the forms are evaluated, and the last value is returned as the result of the cond.
|
|
If the test evaluates to nil, none of the forms are evaluated, and the next argument is processed in the same way.
|
|
*/
|
|
object *tf_cond (object *args, object *env) {
|
|
while (args != NULL) {
|
|
object *clause = first(args);
|
|
if (!consp(clause)) error(illegalclause, clause);
|
|
object *test = eval(first(clause), env);
|
|
object *forms = cdr(clause);
|
|
if (test != nil) {
|
|
if (forms == NULL) return quote(test); else return tf_progn(forms, env);
|
|
}
|
|
args = cdr(args);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(when test form*)
|
|
Evaluates the test. If it's non-nil the forms are evaluated and the last value is returned.
|
|
*/
|
|
object *tf_when (object *args, object *env) {
|
|
if (args == NULL) error2(noargument);
|
|
if (eval(first(args), env) != nil) return tf_progn(cdr(args),env);
|
|
else return nil;
|
|
}
|
|
|
|
/*
|
|
(unless test form*)
|
|
Evaluates the test. If it's nil the forms are evaluated and the last value is returned.
|
|
*/
|
|
object *tf_unless (object *args, object *env) {
|
|
if (args == NULL) error2(noargument);
|
|
if (eval(first(args), env) != nil) return nil;
|
|
else return tf_progn(cdr(args),env);
|
|
}
|
|
|
|
/*
|
|
(case keyform ((key form*) (key form*) ... ))
|
|
Evaluates a keyform to produce a test key, and then tests this against a series of arguments,
|
|
each of which is a list containing a key optionally followed by one or more forms.
|
|
*/
|
|
object *tf_case (object *args, object *env) {
|
|
object *test = eval(first(args), env);
|
|
args = cdr(args);
|
|
while (args != NULL) {
|
|
object *clause = first(args);
|
|
if (!consp(clause)) error(illegalclause, clause);
|
|
object *key = car(clause);
|
|
object *forms = cdr(clause);
|
|
if (consp(key)) {
|
|
while (key != NULL) {
|
|
if (eq(test,car(key))) return tf_progn(forms, env);
|
|
key = cdr(key);
|
|
}
|
|
} else if (eq(test,key) || eq(key,tee)) return tf_progn(forms, env);
|
|
args = cdr(args);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(and item*)
|
|
Evaluates its arguments until one returns nil, and returns the last value.
|
|
*/
|
|
object *tf_and (object *args, object *env) {
|
|
if (args == NULL) return tee;
|
|
object *more = cdr(args);
|
|
while (more != NULL) {
|
|
if (eval(car(args), env) == NULL) return nil;
|
|
args = more;
|
|
more = cdr(args);
|
|
}
|
|
return car(args);
|
|
}
|
|
|
|
// Core functions
|
|
|
|
/*
|
|
(not item)
|
|
Returns t if its argument is nil, or nil otherwise. Equivalent to null.
|
|
*/
|
|
object *fn_not (object *args, object *env) {
|
|
(void) env;
|
|
return (first(args) == nil) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(cons item item)
|
|
If the second argument is a list, cons returns a new list with item added to the front of the list.
|
|
If the second argument isn't a list cons returns a dotted pair.
|
|
*/
|
|
object *fn_cons (object *args, object *env) {
|
|
(void) env;
|
|
return cons(first(args), second(args));
|
|
}
|
|
|
|
/*
|
|
(atom item)
|
|
Returns t if its argument is a single number, symbol, or nil.
|
|
*/
|
|
object *fn_atom (object *args, object *env) {
|
|
(void) env;
|
|
return atom(first(args)) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(listp item)
|
|
Returns t if its argument is a list.
|
|
*/
|
|
object *fn_listp (object *args, object *env) {
|
|
(void) env;
|
|
return listp(first(args)) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(consp item)
|
|
Returns t if its argument is a non-null list.
|
|
*/
|
|
object *fn_consp (object *args, object *env) {
|
|
(void) env;
|
|
return consp(first(args)) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(symbolp item)
|
|
Returns t if its argument is a symbol.
|
|
*/
|
|
object *fn_symbolp (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
return (arg == NULL || symbolp(arg)) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(arrayp item)
|
|
Returns t if its argument is an array.
|
|
*/
|
|
object *fn_arrayp (object *args, object *env) {
|
|
(void) env;
|
|
return arrayp(first(args)) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(boundp item)
|
|
Returns t if its argument is a symbol with a value.
|
|
*/
|
|
object *fn_boundp (object *args, object *env) {
|
|
return boundp(first(args), env) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(keywordp item)
|
|
Returns t if its argument is a built-in or user-defined keyword.
|
|
*/
|
|
object *fn_keywordp (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
if (!symbolp(arg)) return nil;
|
|
return (keywordp(arg) || colonp(arg->name)) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(set symbol value [symbol value]*)
|
|
For each pair of arguments, assigns the value of the second argument to the value of the first argument.
|
|
*/
|
|
object *fn_setfn (object *args, object *env) {
|
|
object *arg = nil;
|
|
while (args != NULL) {
|
|
if (cdr(args) == NULL) error2(oddargs);
|
|
object *pair = findvalue(first(args), env);
|
|
arg = second(args);
|
|
cdr(pair) = arg;
|
|
args = cddr(args);
|
|
}
|
|
return arg;
|
|
}
|
|
|
|
/*
|
|
(streamp item)
|
|
Returns t if its argument is a stream.
|
|
*/
|
|
object *fn_streamp (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
return streamp(arg) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(eq item item)
|
|
Tests whether the two arguments are the same symbol, same character, equal numbers,
|
|
or point to the same cons, and returns t or nil as appropriate.
|
|
*/
|
|
object *fn_eq (object *args, object *env) {
|
|
(void) env;
|
|
return eq(first(args), second(args)) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(equal item item)
|
|
Tests whether the two arguments are the same symbol, same character, equal numbers,
|
|
or point to the same cons, and returns t or nil as appropriate.
|
|
*/
|
|
object *fn_equal (object *args, object *env) {
|
|
(void) env;
|
|
return equal(first(args), second(args)) ? tee : nil;
|
|
}
|
|
|
|
// List functions
|
|
|
|
/*
|
|
(car list)
|
|
Returns the first item in a list.
|
|
*/
|
|
object *fn_car (object *args, object *env) {
|
|
(void) env;
|
|
return carx(first(args));
|
|
}
|
|
|
|
/*
|
|
(cdr list)
|
|
Returns a list with the first item removed.
|
|
*/
|
|
object *fn_cdr (object *args, object *env) {
|
|
(void) env;
|
|
return cdrx(first(args));
|
|
}
|
|
|
|
/*
|
|
(caar list)
|
|
*/
|
|
object *fn_caar (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b100);
|
|
}
|
|
|
|
/*
|
|
(cadr list)
|
|
*/
|
|
object *fn_cadr (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b101);
|
|
}
|
|
|
|
/*
|
|
(cdar list)
|
|
Equivalent to (cdr (car list)).
|
|
*/
|
|
object *fn_cdar (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b110);
|
|
}
|
|
|
|
/*
|
|
(cddr list)
|
|
Equivalent to (cdr (cdr list)).
|
|
*/
|
|
object *fn_cddr (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b111);
|
|
}
|
|
|
|
/*
|
|
(caaar list)
|
|
Equivalent to (car (car (car list))).
|
|
*/
|
|
object *fn_caaar (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b1000);
|
|
}
|
|
|
|
/*
|
|
(caadr list)
|
|
Equivalent to (car (car (cdar list))).
|
|
*/
|
|
object *fn_caadr (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b1001);;
|
|
}
|
|
|
|
/*
|
|
(cadar list)
|
|
Equivalent to (car (cdr (car list))).
|
|
*/
|
|
object *fn_cadar (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b1010);
|
|
}
|
|
|
|
/*
|
|
(caddr list)
|
|
Equivalent to (car (cdr (cdr list))).
|
|
*/
|
|
object *fn_caddr (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b1011);
|
|
}
|
|
|
|
/*
|
|
(cdaar list)
|
|
Equivalent to (cdar (car (car list))).
|
|
*/
|
|
object *fn_cdaar (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b1100);
|
|
}
|
|
|
|
/*
|
|
(cdadr list)
|
|
Equivalent to (cdr (car (cdr list))).
|
|
*/
|
|
object *fn_cdadr (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b1101);
|
|
}
|
|
|
|
/*
|
|
(cddar list)
|
|
Equivalent to (cdr (cdr (car list))).
|
|
*/
|
|
object *fn_cddar (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b1110);
|
|
}
|
|
|
|
/*
|
|
(cdddr list)
|
|
Equivalent to (cdr (cdr (cdr list))).
|
|
*/
|
|
object *fn_cdddr (object *args, object *env) {
|
|
(void) env;
|
|
return cxxxr(args, 0b1111);
|
|
}
|
|
|
|
/*
|
|
(length item)
|
|
Returns the number of items in a list, the length of a string, or the length of a one-dimensional array.
|
|
*/
|
|
object *fn_length (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
if (listp(arg)) return number(listlength(arg));
|
|
if (stringp(arg)) return number(stringlength(arg));
|
|
if (!(arrayp(arg) && cdr(cddr(arg)) == NULL)) error("argument is not a list, 1d array, or string", arg);
|
|
return number(abs(first(cddr(arg))->integer));
|
|
}
|
|
|
|
/*
|
|
(array-dimensions item)
|
|
Returns a list of the dimensions of an array.
|
|
*/
|
|
object *fn_arraydimensions (object *args, object *env) {
|
|
(void) env;
|
|
object *array = first(args);
|
|
if (!arrayp(array)) error("argument is not an array", array);
|
|
object *dimensions = cddr(array);
|
|
return (first(dimensions)->integer < 0) ? cons(number(-(first(dimensions)->integer)), cdr(dimensions)) : dimensions;
|
|
}
|
|
|
|
/*
|
|
(list item*)
|
|
Returns a list of the values of its arguments.
|
|
*/
|
|
object *fn_list (object *args, object *env) {
|
|
(void) env;
|
|
return args;
|
|
}
|
|
|
|
/*
|
|
(copy-list list)
|
|
Returns a copy of a list.
|
|
*/
|
|
object *fn_copylist (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
if (!listp(arg)) error(notalist, arg);
|
|
object *result = cons(NULL, NULL);
|
|
object *ptr = result;
|
|
while (consp(arg)) {
|
|
cdr(ptr) = cons(car(arg), cdr(arg));
|
|
ptr = cdr(ptr); arg = cdr(arg);
|
|
}
|
|
return cdr(result);
|
|
}
|
|
|
|
/*
|
|
(make-array size [:initial-element element] [:element-type 'bit])
|
|
If size is an integer it creates a one-dimensional array with elements from 0 to size-1.
|
|
If size is a list of n integers it creates an n-dimensional array with those dimensions.
|
|
If :element-type 'bit is specified the array is a bit array.
|
|
*/
|
|
object *fn_makearray (object *args, object *env) {
|
|
(void) env;
|
|
object *def = nil;
|
|
bool bitp = false;
|
|
object *dims = first(args);
|
|
if (dims == NULL) error2("dimensions can't be nil");
|
|
else if (atom(dims)) dims = cons(dims, NULL);
|
|
args = cdr(args);
|
|
while (args != NULL && cdr(args) != NULL) {
|
|
object *var = first(args);
|
|
if (isbuiltin(first(args), INITIALELEMENT)) def = second(args);
|
|
else if (isbuiltin(first(args), ELEMENTTYPE) && isbuiltin(second(args), BIT)) bitp = true;
|
|
else error("argument not recognised", var);
|
|
args = cddr(args);
|
|
}
|
|
if (bitp) {
|
|
if (def == nil) def = number(0);
|
|
else def = number(-checkbitvalue(def)); // 1 becomes all ones
|
|
}
|
|
return makearray(dims, def, bitp);
|
|
}
|
|
|
|
/*
|
|
(reverse list)
|
|
Returns a list with the elements of list in reverse order.
|
|
*/
|
|
object *fn_reverse (object *args, object *env) {
|
|
(void) env;
|
|
object *list = first(args);
|
|
object *result = NULL;
|
|
while (list != NULL) {
|
|
if (improperp(list)) error(notproper, list);
|
|
push(first(list),result);
|
|
list = cdr(list);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
(nth number list)
|
|
Returns the nth item in list, counting from zero.
|
|
*/
|
|
object *fn_nth (object *args, object *env) {
|
|
(void) env;
|
|
int n = checkinteger(first(args));
|
|
if (n < 0) error(indexnegative, first(args));
|
|
object *list = second(args);
|
|
while (list != NULL) {
|
|
if (improperp(list)) error(notproper, list);
|
|
if (n == 0) return car(list);
|
|
list = cdr(list);
|
|
n--;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(aref array index [index*])
|
|
Returns an element from the specified array.
|
|
*/
|
|
object *fn_aref (object *args, object *env) {
|
|
(void) env;
|
|
int bit;
|
|
object *array = first(args);
|
|
if (!arrayp(array)) error("first argument is not an array", array);
|
|
object *loc = *getarray(array, cdr(args), 0, &bit);
|
|
if (bit == -1) return loc;
|
|
else return number((loc->integer)>>bit & 1);
|
|
}
|
|
|
|
/*
|
|
(assoc key list [:test function])
|
|
Looks up a key in an association list of (key . value) pairs, using eq or the specified test function,
|
|
and returns the matching pair, or nil if no pair is found.
|
|
*/
|
|
object *fn_assoc (object *args, object *env) {
|
|
(void) env;
|
|
object *key = first(args);
|
|
object *list = second(args);
|
|
object *test = testargument(cddr(args));
|
|
while (list != NULL) {
|
|
if (improperp(list)) error(notproper, list);
|
|
object *pair = first(list);
|
|
if (!listp(pair)) error("element is not a list", pair);
|
|
if (pair != NULL && apply(test, cons(key, cons(car(pair), NULL)), env) != NULL) return pair;
|
|
list = cdr(list);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(member item list [:test function])
|
|
Searches for an item in a list, using eq or the specified test function, and returns the list starting
|
|
from the first occurrence of the item, or nil if it is not found.
|
|
*/
|
|
object *fn_member (object *args, object *env) {
|
|
(void) env;
|
|
object *item = first(args);
|
|
object *list = second(args);
|
|
object *test = testargument(cddr(args));
|
|
while (list != NULL) {
|
|
if (improperp(list)) error(notproper, list);
|
|
if (apply(test, cons(item, cons(car(list), NULL)), env) != NULL) return list;
|
|
list = cdr(list);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(apply function list)
|
|
Returns the result of evaluating function, with the list of arguments specified by the second parameter.
|
|
*/
|
|
object *fn_apply (object *args, object *env) {
|
|
object *previous = NULL;
|
|
object *last = args;
|
|
while (cdr(last) != NULL) {
|
|
previous = last;
|
|
last = cdr(last);
|
|
}
|
|
object *arg = car(last);
|
|
if (!listp(arg)) error(notalist, arg);
|
|
cdr(previous) = arg;
|
|
return apply(first(args), cdr(args), env);
|
|
}
|
|
|
|
/*
|
|
(funcall function argument*)
|
|
Evaluates function with the specified arguments.
|
|
*/
|
|
object *fn_funcall (object *args, object *env) {
|
|
return apply(first(args), cdr(args), env);
|
|
}
|
|
|
|
/*
|
|
(append list*)
|
|
Joins its arguments, which should be lists, into a single list.
|
|
*/
|
|
object *fn_append (object *args, object *env) {
|
|
(void) env;
|
|
object *head = NULL;
|
|
object *tail;
|
|
while (args != NULL) {
|
|
object *list = first(args);
|
|
if (!listp(list)) error(notalist, list);
|
|
while (consp(list)) {
|
|
object *obj = cons(car(list), cdr(list));
|
|
if (head == NULL) head = obj;
|
|
else cdr(tail) = obj;
|
|
tail = obj;
|
|
list = cdr(list);
|
|
if (cdr(args) != NULL && improperp(list)) error(notproper, first(args));
|
|
}
|
|
args = cdr(args);
|
|
}
|
|
return head;
|
|
}
|
|
|
|
/*
|
|
(mapc function list1 [list]*)
|
|
Applies the function to each element in one or more lists, ignoring the results.
|
|
It returns the first list argument.
|
|
*/
|
|
object *fn_mapc (object *args, object *env) {
|
|
return mapcl(args, env, false);
|
|
}
|
|
|
|
/*
|
|
(mapl function list1 [list]*)
|
|
Applies the function to one or more lists and then successive cdrs of those lists,
|
|
ignoring the results. It returns the first list argument.
|
|
*/
|
|
object *fn_mapl (object *args, object *env) {
|
|
return mapcl(args, env, true);
|
|
}
|
|
|
|
/*
|
|
(mapcar function list1 [list]*)
|
|
Applies the function to each element in one or more lists, and returns the resulting list.
|
|
*/
|
|
object *fn_mapcar (object *args, object *env) {
|
|
return mapcarcan(args, env, mapcarfun, false);
|
|
}
|
|
|
|
/*
|
|
(mapcan function list1 [list]*)
|
|
Applies the function to each element in one or more lists. The results should be lists,
|
|
and these are destructively concatenated together to give the value returned.
|
|
*/
|
|
object *fn_mapcan (object *args, object *env) {
|
|
return mapcarcan(args, env, mapcanfun, false);
|
|
}
|
|
|
|
/*
|
|
(maplist function list1 [list]*)
|
|
Applies the function to one or more lists and then successive cdrs of those lists,
|
|
and returns the resulting list.
|
|
*/
|
|
object *fn_maplist (object *args, object *env) {
|
|
return mapcarcan(args, env, mapcarfun, true);
|
|
}
|
|
|
|
/*
|
|
(mapcon function list1 [list]*)
|
|
Applies the function to one or more lists and then successive cdrs of those lists,
|
|
and these are destructively concatenated together to give the value returned.
|
|
*/
|
|
object *fn_mapcon (object *args, object *env) {
|
|
return mapcarcan(args, env, mapcanfun, true);
|
|
}
|
|
|
|
// Arithmetic functions
|
|
|
|
/*
|
|
(+ number*)
|
|
Adds its arguments together.
|
|
If each argument is an integer, and the running total doesn't overflow, the result is an integer,
|
|
otherwise a floating-point number.
|
|
*/
|
|
object *fn_add (object *args, object *env) {
|
|
(void) env;
|
|
int result = 0;
|
|
while (args != NULL) {
|
|
object *arg = car(args);
|
|
if (floatp(arg)) return add_floats(args, (float)result);
|
|
else if (integerp(arg)) {
|
|
int val = arg->integer;
|
|
if (val < 1) { if (INT_MIN - val > result) return add_floats(args, (float)result); }
|
|
else { if (INT_MAX - val < result) return add_floats(args, (float)result); }
|
|
result = result + val;
|
|
} else error(notanumber, arg);
|
|
args = cdr(args);
|
|
}
|
|
return number(result);
|
|
}
|
|
|
|
/*
|
|
(- number*)
|
|
If there is one argument, negates the argument.
|
|
If there are two or more arguments, subtracts the second and subsequent arguments from the first argument.
|
|
If each argument is an integer, and the running total doesn't overflow, returns the result as an integer,
|
|
otherwise a floating-point number.
|
|
*/
|
|
object *fn_subtract (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = car(args);
|
|
args = cdr(args);
|
|
if (args == NULL) return negate(arg);
|
|
else if (floatp(arg)) return subtract_floats(args, arg->single_float);
|
|
else if (integerp(arg)) {
|
|
int result = arg->integer;
|
|
while (args != NULL) {
|
|
arg = car(args);
|
|
if (floatp(arg)) return subtract_floats(args, result);
|
|
else if (integerp(arg)) {
|
|
int val = (car(args))->integer;
|
|
if (val < 1) { if (INT_MAX + val < result) return subtract_floats(args, result); }
|
|
else { if (INT_MIN + val > result) return subtract_floats(args, result); }
|
|
result = result - val;
|
|
} else error(notanumber, arg);
|
|
args = cdr(args);
|
|
}
|
|
return number(result);
|
|
} else error(notanumber, arg);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(* number*)
|
|
Multiplies its arguments together.
|
|
If each argument is an integer, and the running total doesn't overflow, the result is an integer,
|
|
otherwise it's a floating-point number.
|
|
*/
|
|
object *fn_multiply (object *args, object *env) {
|
|
(void) env;
|
|
int result = 1;
|
|
while (args != NULL){
|
|
object *arg = car(args);
|
|
if (floatp(arg)) return multiply_floats(args, result);
|
|
else if (integerp(arg)) {
|
|
int64_t val = result * (int64_t)(arg->integer);
|
|
if ((val > INT_MAX) || (val < INT_MIN)) return multiply_floats(args, result);
|
|
result = val;
|
|
} else error(notanumber, arg);
|
|
args = cdr(args);
|
|
}
|
|
return number(result);
|
|
}
|
|
|
|
/*
|
|
(/ number*)
|
|
Divides the first argument by the second and subsequent arguments.
|
|
If each argument is an integer, and each division produces an exact result, the result is an integer;
|
|
otherwise it's a floating-point number.
|
|
*/
|
|
object *fn_divide (object *args, object *env) {
|
|
(void) env;
|
|
object* arg = first(args);
|
|
args = cdr(args);
|
|
// One argument
|
|
if (args == NULL) {
|
|
if (floatp(arg)) {
|
|
float f = arg->single_float;
|
|
if (f == 0.0) error2(divisionbyzero);
|
|
return makefloat(1.0 / f);
|
|
} else if (integerp(arg)) {
|
|
int i = arg->integer;
|
|
if (i == 0) error2(divisionbyzero);
|
|
else if (i == 1) return number(1);
|
|
else return makefloat(1.0 / i);
|
|
} else error(notanumber, arg);
|
|
}
|
|
// Multiple arguments
|
|
if (floatp(arg)) return divide_floats(args, arg->single_float);
|
|
else if (integerp(arg)) {
|
|
int result = arg->integer;
|
|
while (args != NULL) {
|
|
arg = car(args);
|
|
if (floatp(arg)) {
|
|
return divide_floats(args, result);
|
|
} else if (integerp(arg)) {
|
|
int i = arg->integer;
|
|
if (i == 0) error2(divisionbyzero);
|
|
if ((result % i) != 0) return divide_floats(args, result);
|
|
if ((result == INT_MIN) && (i == -1)) return divide_floats(args, result);
|
|
result = result / i;
|
|
args = cdr(args);
|
|
} else error(notanumber, arg);
|
|
}
|
|
return number(result);
|
|
} else error(notanumber, arg);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(mod number number)
|
|
Returns its first argument modulo the second argument.
|
|
If both arguments are integers the result is an integer; otherwise it's a floating-point number.
|
|
*/
|
|
object *fn_mod (object *args, object *env) {
|
|
(void) env;
|
|
return remmod(args, true);
|
|
}
|
|
|
|
/*
|
|
(rem number number)
|
|
Returns the remainder from dividing the first argument by the second argument.
|
|
If both arguments are integers the result is an integer; otherwise it's a floating-point number.
|
|
*/
|
|
object *fn_rem (object *args, object *env) {
|
|
(void) env;
|
|
return remmod(args, false);
|
|
}
|
|
|
|
/*
|
|
(1+ number)
|
|
Adds one to its argument and returns it.
|
|
If the argument is an integer the result is an integer if possible;
|
|
otherwise it's a floating-point number.
|
|
*/
|
|
object *fn_oneplus (object *args, object *env) {
|
|
(void) env;
|
|
object* arg = first(args);
|
|
if (floatp(arg)) return makefloat((arg->single_float) + 1.0);
|
|
else if (integerp(arg)) {
|
|
int result = arg->integer;
|
|
if (result == INT_MAX) return makefloat((arg->integer) + 1.0);
|
|
else return number(result + 1);
|
|
} else error(notanumber, arg);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(1- number)
|
|
Subtracts one from its argument and returns it.
|
|
If the argument is an integer the result is an integer if possible;
|
|
otherwise it's a floating-point number.
|
|
*/
|
|
object *fn_oneminus (object *args, object *env) {
|
|
(void) env;
|
|
object* arg = first(args);
|
|
if (floatp(arg)) return makefloat((arg->single_float) - 1.0);
|
|
else if (integerp(arg)) {
|
|
int result = arg->integer;
|
|
if (result == INT_MIN) return makefloat((arg->integer) - 1.0);
|
|
else return number(result - 1);
|
|
} else error(notanumber, arg);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(abs number)
|
|
Returns the absolute, positive value of its argument.
|
|
If the argument is an integer the result will be returned as an integer if possible,
|
|
otherwise a floating-point number.
|
|
*/
|
|
object *fn_abs (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
if (floatp(arg)) return makefloat(abs(arg->single_float));
|
|
else if (integerp(arg)) {
|
|
int result = arg->integer;
|
|
if (result == INT_MIN) return makefloat(abs((float)result));
|
|
else return number(abs(result));
|
|
} else error(notanumber, arg);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(random number)
|
|
If number is an integer returns a random number between 0 and one less than its argument.
|
|
Otherwise returns a floating-point number between zero and number.
|
|
*/
|
|
object *fn_random (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
if (integerp(arg)) return number(random(arg->integer));
|
|
else if (floatp(arg)) return makefloat((float)rand()/(float)(RAND_MAX/(arg->single_float)));
|
|
else error(notanumber, arg);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(max number*)
|
|
Returns the maximum of one or more arguments.
|
|
*/
|
|
object *fn_maxfn (object *args, object *env) {
|
|
(void) env;
|
|
object* result = first(args);
|
|
args = cdr(args);
|
|
while (args != NULL) {
|
|
object *arg = car(args);
|
|
if (integerp(result) && integerp(arg)) {
|
|
if ((arg->integer) > (result->integer)) result = arg;
|
|
} else if ((checkintfloat(arg) > checkintfloat(result))) result = arg;
|
|
args = cdr(args);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
(min number*)
|
|
Returns the minimum of one or more arguments.
|
|
*/
|
|
object *fn_minfn (object *args, object *env) {
|
|
(void) env;
|
|
object* result = first(args);
|
|
args = cdr(args);
|
|
while (args != NULL) {
|
|
object *arg = car(args);
|
|
if (integerp(result) && integerp(arg)) {
|
|
if ((arg->integer) < (result->integer)) result = arg;
|
|
} else if ((checkintfloat(arg) < checkintfloat(result))) result = arg;
|
|
args = cdr(args);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Arithmetic comparisons
|
|
|
|
/*
|
|
(/= number*)
|
|
Returns t if none of the arguments are equal, or nil if two or more arguments are equal.
|
|
*/
|
|
object *fn_noteq (object *args, object *env) {
|
|
(void) env;
|
|
while (args != NULL) {
|
|
object *nargs = args;
|
|
object *arg1 = first(nargs);
|
|
nargs = cdr(nargs);
|
|
while (nargs != NULL) {
|
|
object *arg2 = first(nargs);
|
|
if (integerp(arg1) && integerp(arg2)) {
|
|
if ((arg1->integer) == (arg2->integer)) return nil;
|
|
} else if ((checkintfloat(arg1) == checkintfloat(arg2))) return nil;
|
|
nargs = cdr(nargs);
|
|
}
|
|
args = cdr(args);
|
|
}
|
|
return tee;
|
|
}
|
|
|
|
/*
|
|
(= number*)
|
|
Returns t if all the arguments, which must be numbers, are numerically equal, and nil otherwise.
|
|
*/
|
|
object *fn_numeq (object *args, object *env) {
|
|
(void) env;
|
|
return compare(args, false, false, true);
|
|
}
|
|
|
|
/*
|
|
(< number*)
|
|
Returns t if each argument is less than the next argument, and nil otherwise.
|
|
*/
|
|
object *fn_less (object *args, object *env) {
|
|
(void) env;
|
|
return compare(args, true, false, false);
|
|
}
|
|
|
|
/*
|
|
(<= number*)
|
|
Returns t if each argument is less than or equal to the next argument, and nil otherwise.
|
|
*/
|
|
object *fn_lesseq (object *args, object *env) {
|
|
(void) env;
|
|
return compare(args, true, false, true);
|
|
}
|
|
|
|
/*
|
|
(> number*)
|
|
Returns t if each argument is greater than the next argument, and nil otherwise.
|
|
*/
|
|
object *fn_greater (object *args, object *env) {
|
|
(void) env;
|
|
return compare(args, false, true, false);
|
|
}
|
|
|
|
/*
|
|
(>= number*)
|
|
Returns t if each argument is greater than or equal to the next argument, and nil otherwise.
|
|
*/
|
|
object *fn_greatereq (object *args, object *env) {
|
|
(void) env;
|
|
return compare(args, false, true, true);
|
|
}
|
|
|
|
/*
|
|
(plusp number)
|
|
Returns t if the argument is greater than zero, or nil otherwise.
|
|
*/
|
|
object *fn_plusp (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
if (floatp(arg)) return ((arg->single_float) > 0.0) ? tee : nil;
|
|
else if (integerp(arg)) return ((arg->integer) > 0) ? tee : nil;
|
|
else error(notanumber, arg);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(minusp number)
|
|
Returns t if the argument is less than zero, or nil otherwise.
|
|
*/
|
|
object *fn_minusp (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
if (floatp(arg)) return ((arg->single_float) < 0.0) ? tee : nil;
|
|
else if (integerp(arg)) return ((arg->integer) < 0) ? tee : nil;
|
|
else error(notanumber, arg);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(zerop number)
|
|
Returns t if the argument is zero.
|
|
*/
|
|
object *fn_zerop (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
if (floatp(arg)) return ((arg->single_float) == 0.0) ? tee : nil;
|
|
else if (integerp(arg)) return ((arg->integer) == 0) ? tee : nil;
|
|
else error(notanumber, arg);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(oddp number)
|
|
Returns t if the integer argument is odd.
|
|
*/
|
|
object *fn_oddp (object *args, object *env) {
|
|
(void) env;
|
|
int arg = checkinteger(first(args));
|
|
return ((arg & 1) == 1) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(evenp number)
|
|
Returns t if the integer argument is even.
|
|
*/
|
|
object *fn_evenp (object *args, object *env) {
|
|
(void) env;
|
|
int arg = checkinteger(first(args));
|
|
return ((arg & 1) == 0) ? tee : nil;
|
|
}
|
|
|
|
// Number functions
|
|
|
|
/*
|
|
(integerp number)
|
|
Returns t if the argument is an integer.
|
|
*/
|
|
object *fn_integerp (object *args, object *env) {
|
|
(void) env;
|
|
return integerp(first(args)) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(numberp number)
|
|
Returns t if the argument is a number.
|
|
*/
|
|
object *fn_numberp (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
return (integerp(arg) || floatp(arg)) ? tee : nil;
|
|
}
|
|
|
|
// Floating-point functions
|
|
|
|
/*
|
|
(float number)
|
|
Returns its argument converted to a floating-point number.
|
|
*/
|
|
object *fn_floatfn (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
return (floatp(arg)) ? arg : makefloat((float)(arg->integer));
|
|
}
|
|
|
|
/*
|
|
(floatp number)
|
|
Returns t if the argument is a floating-point number.
|
|
*/
|
|
object *fn_floatp (object *args, object *env) {
|
|
(void) env;
|
|
return floatp(first(args)) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(sin number)
|
|
Returns sin(number).
|
|
*/
|
|
object *fn_sin (object *args, object *env) {
|
|
(void) env;
|
|
return makefloat(sin(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(cos number)
|
|
Returns cos(number).
|
|
*/
|
|
object *fn_cos (object *args, object *env) {
|
|
(void) env;
|
|
return makefloat(cos(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(tan number)
|
|
Returns tan(number).
|
|
*/
|
|
object *fn_tan (object *args, object *env) {
|
|
(void) env;
|
|
return makefloat(tan(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(asin number)
|
|
Returns asin(number).
|
|
*/
|
|
object *fn_asin (object *args, object *env) {
|
|
(void) env;
|
|
return makefloat(asin(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(acos number)
|
|
Returns acos(number).
|
|
*/
|
|
object *fn_acos (object *args, object *env) {
|
|
(void) env;
|
|
return makefloat(acos(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(atan number1 [number2])
|
|
Returns the arc tangent of number1/number2, in radians. If number2 is omitted it defaults to 1.
|
|
*/
|
|
object *fn_atan (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
float div = 1.0;
|
|
args = cdr(args);
|
|
if (args != NULL) div = checkintfloat(first(args));
|
|
return makefloat(atan2(checkintfloat(arg), div));
|
|
}
|
|
|
|
/*
|
|
(sinh number)
|
|
Returns sinh(number).
|
|
*/
|
|
object *fn_sinh (object *args, object *env) {
|
|
(void) env;
|
|
return makefloat(sinh(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(cosh number)
|
|
Returns cosh(number).
|
|
*/
|
|
object *fn_cosh (object *args, object *env) {
|
|
(void) env;
|
|
return makefloat(cosh(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(tanh number)
|
|
Returns tanh(number).
|
|
*/
|
|
object *fn_tanh (object *args, object *env) {
|
|
(void) env;
|
|
return makefloat(tanh(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(exp number)
|
|
Returns exp(number).
|
|
*/
|
|
object *fn_exp (object *args, object *env) {
|
|
(void) env;
|
|
return makefloat(exp(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(sqrt number)
|
|
Returns sqrt(number).
|
|
*/
|
|
object *fn_sqrt (object *args, object *env) {
|
|
(void) env;
|
|
return makefloat(sqrt(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(log number [base])
|
|
Returns the logarithm of number to the specified base. If base is omitted it defaults to e.
|
|
*/
|
|
object *fn_log (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
float fresult = log(checkintfloat(arg));
|
|
args = cdr(args);
|
|
if (args == NULL) return makefloat(fresult);
|
|
else return makefloat(fresult / log(checkintfloat(first(args))));
|
|
}
|
|
|
|
/*
|
|
(expt number power)
|
|
Returns number raised to the specified power.
|
|
Returns the result as an integer if the arguments are integers and the result will be within range,
|
|
otherwise a floating-point number.
|
|
*/
|
|
object *fn_expt (object *args, object *env) {
|
|
(void) env;
|
|
object *arg1 = first(args); object *arg2 = second(args);
|
|
float float1 = checkintfloat(arg1);
|
|
float value = log(abs(float1)) * checkintfloat(arg2);
|
|
if (integerp(arg1) && integerp(arg2) && ((arg2->integer) >= 0) && (abs(value) < 21.4875))
|
|
return number(intpower(arg1->integer, arg2->integer));
|
|
if (float1 < 0) {
|
|
if (integerp(arg2)) return makefloat((arg2->integer & 1) ? -exp(value) : exp(value));
|
|
else error2("invalid result");
|
|
}
|
|
return makefloat(exp(value));
|
|
}
|
|
|
|
/*
|
|
(ceiling number [divisor])
|
|
Returns ceil(number/divisor). If omitted, divisor is 1.
|
|
*/
|
|
object *fn_ceiling (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
args = cdr(args);
|
|
if (args != NULL) return number(ceil(checkintfloat(arg) / checkintfloat(first(args))));
|
|
else return number(ceil(checkintfloat(arg)));
|
|
}
|
|
|
|
/*
|
|
(floor number [divisor])
|
|
Returns floor(number/divisor). If omitted, divisor is 1.
|
|
*/
|
|
object *fn_floor (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
args = cdr(args);
|
|
if (args != NULL) return number(floor(checkintfloat(arg) / checkintfloat(first(args))));
|
|
else return number(floor(checkintfloat(arg)));
|
|
}
|
|
|
|
/*
|
|
(truncate number [divisor])
|
|
Returns the integer part of number/divisor. If divisor is omitted it defaults to 1.
|
|
*/
|
|
object *fn_truncate (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
args = cdr(args);
|
|
if (args != NULL) return number((int)(checkintfloat(arg) / checkintfloat(first(args))));
|
|
else return number((int)(checkintfloat(arg)));
|
|
}
|
|
|
|
/*
|
|
(round number [divisor])
|
|
Returns the integer closest to number/divisor. If divisor is omitted it defaults to 1.
|
|
*/
|
|
object *fn_round (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
args = cdr(args);
|
|
if (args != NULL) return number(round(checkintfloat(arg) / checkintfloat(first(args))));
|
|
else return number(round(checkintfloat(arg)));
|
|
}
|
|
|
|
// Characters
|
|
|
|
/*
|
|
(char string n)
|
|
Returns the nth character in a string, counting from zero.
|
|
*/
|
|
object *fn_char (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
if (!stringp(arg)) error(notastring, arg);
|
|
object *n = second(args);
|
|
char c = nthchar(arg, checkinteger(n));
|
|
if (c == 0) error(indexrange, n);
|
|
return character(c);
|
|
}
|
|
|
|
/*
|
|
(char-code character)
|
|
Returns the ASCII code for a character, as an integer.
|
|
*/
|
|
object *fn_charcode (object *args, object *env) {
|
|
(void) env;
|
|
return number(checkchar(first(args)));
|
|
}
|
|
|
|
/*
|
|
(code-char integer)
|
|
Returns the character for the specified ASCII code.
|
|
*/
|
|
object *fn_codechar (object *args, object *env) {
|
|
(void) env;
|
|
return character(checkinteger(first(args)));
|
|
}
|
|
|
|
/*
|
|
(characterp item)
|
|
Returns t if the argument is a character and nil otherwise.
|
|
*/
|
|
object *fn_characterp (object *args, object *env) {
|
|
(void) env;
|
|
return characterp(first(args)) ? tee : nil;
|
|
}
|
|
|
|
// Strings
|
|
|
|
/*
|
|
(stringp item)
|
|
Returns t if the argument is a string and nil otherwise.
|
|
*/
|
|
object *fn_stringp (object *args, object *env) {
|
|
(void) env;
|
|
return stringp(first(args)) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(string= string string)
|
|
Returns t if the two strings are the same, or nil otherwise.
|
|
*/
|
|
object *fn_stringeq (object *args, object *env) {
|
|
(void) env;
|
|
int m = stringcompare(args, false, false, true);
|
|
return m == -1 ? nil : tee;
|
|
}
|
|
|
|
/*
|
|
(string< string string)
|
|
Returns the index to the first mismatch if the first string is alphabetically less than the second string,
|
|
or nil otherwise.
|
|
*/
|
|
object *fn_stringless (object *args, object *env) {
|
|
(void) env;
|
|
int m = stringcompare(args, true, false, false);
|
|
return m == -1 ? nil : number(m);
|
|
}
|
|
|
|
/*
|
|
(string> string string)
|
|
Returns the index to the first mismatch if the first string is alphabetically greater than the second string,
|
|
or nil otherwise.
|
|
*/
|
|
object *fn_stringgreater (object *args, object *env) {
|
|
(void) env;
|
|
int m = stringcompare(args, false, true, false);
|
|
return m == -1 ? nil : number(m);
|
|
}
|
|
|
|
/*
|
|
(string/= string string)
|
|
Returns the index to the first mismatch if the two strings are not the same, or nil otherwise.
|
|
*/
|
|
object *fn_stringnoteq (object *args, object *env) {
|
|
(void) env;
|
|
int m = stringcompare(args, true, true, false);
|
|
return m == -1 ? nil : number(m);
|
|
}
|
|
|
|
/*
|
|
(string<= string string)
|
|
Returns the index to the first mismatch if the first string is alphabetically less than or equal to
|
|
the second string, or nil otherwise.
|
|
*/
|
|
object *fn_stringlesseq (object *args, object *env) {
|
|
(void) env;
|
|
int m = stringcompare(args, true, false, true);
|
|
return m == -1 ? nil : number(m);
|
|
}
|
|
|
|
/*
|
|
(string>= string string)
|
|
Returns the index to the first mismatch if the first string is alphabetically greater than or equal to
|
|
the second string, or nil otherwise.
|
|
*/
|
|
object *fn_stringgreatereq (object *args, object *env) {
|
|
(void) env;
|
|
int m = stringcompare(args, false, true, true);
|
|
return m == -1 ? nil : number(m);
|
|
}
|
|
|
|
/*
|
|
(sort list test)
|
|
Destructively sorts list according to the test function, using an insertion sort, and returns the sorted list.
|
|
*/
|
|
object *fn_sort (object *args, object *env) {
|
|
object *arg = first(args);
|
|
if (!listp(arg)) error(notalist, arg);
|
|
if (arg == NULL) return nil;
|
|
object *list = cons(nil, arg);
|
|
protect(list);
|
|
object *predicate = second(args);
|
|
object *compare = cons(NULL, cons(NULL, NULL));
|
|
protect(compare);
|
|
object *ptr = cdr(list);
|
|
while (cdr(ptr) != NULL) {
|
|
object *go = list;
|
|
while (go != ptr) {
|
|
car(compare) = car(cdr(ptr));
|
|
car(cdr(compare)) = car(cdr(go));
|
|
if (apply(predicate, compare, env)) break;
|
|
go = cdr(go);
|
|
}
|
|
if (go != ptr) {
|
|
object *obj = cdr(ptr);
|
|
cdr(ptr) = cdr(obj);
|
|
cdr(obj) = cdr(go);
|
|
cdr(go) = obj;
|
|
} else ptr = cdr(ptr);
|
|
}
|
|
unprotect(); unprotect();
|
|
return cdr(list);
|
|
}
|
|
|
|
/*
|
|
(string item)
|
|
Converts its argument to a string.
|
|
*/
|
|
object *fn_stringfn (object *args, object *env) {
|
|
return fn_princtostring(args, env);
|
|
}
|
|
|
|
/*
|
|
(concatenate 'string string*)
|
|
Joins together the strings given in the second and subsequent arguments, and returns a single string.
|
|
*/
|
|
object *fn_concatenate (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
if (builtin(arg->name) != STRINGFN) error2("only supports strings");
|
|
args = cdr(args);
|
|
object *result = newstring();
|
|
object *tail = result;
|
|
while (args != NULL) {
|
|
object *obj = checkstring(first(args));
|
|
obj = cdr(obj);
|
|
while (obj != NULL) {
|
|
int quad = obj->chars;
|
|
while (quad != 0) {
|
|
char ch = quad>>((sizeof(int)-1)*8) & 0xFF;
|
|
buildstring(ch, &tail);
|
|
quad = quad<<8;
|
|
}
|
|
obj = car(obj);
|
|
}
|
|
args = cdr(args);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
(subseq seq start [end])
|
|
Returns a subsequence of a list or string from item start to item end-1.
|
|
*/
|
|
object *fn_subseq (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
int start = checkinteger(second(args)), end;
|
|
if (start < 0) error(indexnegative, second(args));
|
|
args = cddr(args);
|
|
if (listp(arg)) {
|
|
int length = listlength(arg);
|
|
if (args != NULL) end = checkinteger(car(args)); else end = length;
|
|
if (start > end || end > length) error2(indexrange);
|
|
object *result = cons(NULL, NULL);
|
|
object *ptr = result;
|
|
for (int x = 0; x < end; x++) {
|
|
if (x >= start) { cdr(ptr) = cons(car(arg), NULL); ptr = cdr(ptr); }
|
|
arg = cdr(arg);
|
|
}
|
|
return cdr(result);
|
|
} else if (stringp(arg)) {
|
|
int length = stringlength(arg);
|
|
if (args != NULL) end = checkinteger(car(args)); else end = length;
|
|
if (start > end || end > length) error2(indexrange);
|
|
object *result = newstring();
|
|
object *tail = result;
|
|
for (int i=start; i<end; i++) {
|
|
char ch = nthchar(arg, i);
|
|
buildstring(ch, &tail);
|
|
}
|
|
return result;
|
|
} else error2("argument is not a list or string");
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(search pattern target [:test function])
|
|
Returns the index of the first occurrence of pattern in target, or nil if it's not found.
|
|
The target can be a list or string. If it's a list a test function can be specified; default eq.
|
|
*/
|
|
object *fn_search (object *args, object *env) {
|
|
(void) env;
|
|
object *pattern = first(args);
|
|
object *target = second(args);
|
|
if (pattern == NULL) return number(0);
|
|
else if (target == NULL) return nil;
|
|
|
|
else if (listp(pattern) && listp(target)) {
|
|
object *test = testargument(cddr(args));
|
|
int l = listlength(target);
|
|
int m = listlength(pattern);
|
|
for (int i = 0; i <= l-m; i++) {
|
|
object *target1 = target;
|
|
while (pattern != NULL && apply(test, cons(car(target1), cons(car(pattern), NULL)), env) != NULL) {
|
|
pattern = cdr(pattern);
|
|
target1 = cdr(target1);
|
|
}
|
|
if (pattern == NULL) return number(i);
|
|
pattern = first(args); target = cdr(target);
|
|
}
|
|
return nil;
|
|
|
|
} else if (stringp(pattern) && stringp(target)) {
|
|
if (cddr(args) != NULL) error2("keyword argument not supported for strings");
|
|
int l = stringlength(target);
|
|
int m = stringlength(pattern);
|
|
for (int i = 0; i <= l-m; i++) {
|
|
int j = 0;
|
|
while (j < m && nthchar(target, i+j) == nthchar(pattern, j)) j++;
|
|
if (j == m) return number(i);
|
|
}
|
|
return nil;
|
|
} else error2("arguments are not both lists or strings");
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(read-from-string string)
|
|
Reads an atom or list from the specified string and returns it.
|
|
*/
|
|
object *fn_readfromstring (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = checkstring(first(args));
|
|
GlobalString = arg;
|
|
GlobalStringIndex = 0;
|
|
object *val = read(gstr);
|
|
LastChar = 0;
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
(princ-to-string item)
|
|
Prints its argument to a string, and returns the string.
|
|
Characters and strings are printed without quotation marks or escape characters.
|
|
*/
|
|
object *fn_princtostring (object *args, object *env) {
|
|
(void) env;
|
|
return princtostring(first(args));
|
|
}
|
|
|
|
/*
|
|
(prin1-to-string item [stream])
|
|
Prints its argument to a string, and returns the string.
|
|
Characters and strings are printed with quotation marks and escape characters,
|
|
in a format that will be suitable for read-from-string.
|
|
*/
|
|
object *fn_prin1tostring (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
object *obj = startstring();
|
|
printobject(arg, pstr);
|
|
return obj;
|
|
}
|
|
|
|
// Bitwise operators
|
|
|
|
/*
|
|
(logand [value*])
|
|
Returns the bitwise & of the values.
|
|
*/
|
|
object *fn_logand (object *args, object *env) {
|
|
(void) env;
|
|
int result = -1;
|
|
while (args != NULL) {
|
|
result = result & checkinteger(first(args));
|
|
args = cdr(args);
|
|
}
|
|
return number(result);
|
|
}
|
|
|
|
/*
|
|
(logior [value*])
|
|
Returns the bitwise | of the values.
|
|
*/
|
|
object *fn_logior (object *args, object *env) {
|
|
(void) env;
|
|
int result = 0;
|
|
while (args != NULL) {
|
|
result = result | checkinteger(first(args));
|
|
args = cdr(args);
|
|
}
|
|
return number(result);
|
|
}
|
|
|
|
/*
|
|
(logxor [value*])
|
|
Returns the bitwise ^ of the values.
|
|
*/
|
|
object *fn_logxor (object *args, object *env) {
|
|
(void) env;
|
|
int result = 0;
|
|
while (args != NULL) {
|
|
result = result ^ checkinteger(first(args));
|
|
args = cdr(args);
|
|
}
|
|
return number(result);
|
|
}
|
|
|
|
/*
|
|
(lognot value)
|
|
Returns the bitwise logical NOT of the value.
|
|
*/
|
|
object *fn_lognot (object *args, object *env) {
|
|
(void) env;
|
|
int result = checkinteger(car(args));
|
|
return number(~result);
|
|
}
|
|
|
|
/*
|
|
(ash value shift)
|
|
Returns the result of bitwise shifting value by shift bits. If shift is positive, value is shifted to the left.
|
|
*/
|
|
object *fn_ash (object *args, object *env) {
|
|
(void) env;
|
|
int value = checkinteger(first(args));
|
|
int count = checkinteger(second(args));
|
|
if (count >= 0) return number(value << count);
|
|
else return number(value >> abs(count));
|
|
}
|
|
|
|
/*
|
|
(logbitp bit value)
|
|
Returns t if bit number bit in value is a '1', and nil if it is a '0'.
|
|
*/
|
|
object *fn_logbitp (object *args, object *env) {
|
|
(void) env;
|
|
int index = checkinteger(first(args));
|
|
int value = checkinteger(second(args));
|
|
return (bitRead(value, index) == 1) ? tee : nil;
|
|
}
|
|
|
|
// System functions
|
|
|
|
/*
|
|
(eval form*)
|
|
Evaluates its argument an extra time.
|
|
*/
|
|
object *fn_eval (object *args, object *env) {
|
|
return eval(first(args), env);
|
|
}
|
|
|
|
/*
|
|
(return [value])
|
|
Exits from a (dotimes ...), (dolist ...), or (loop ...) loop construct and returns value.
|
|
*/
|
|
object *fn_return (object *args, object *env) {
|
|
(void) env;
|
|
setflag(RETURNFLAG);
|
|
if (args == NULL) return nil; else return first(args);
|
|
}
|
|
|
|
/*
|
|
(globals)
|
|
Returns a list of global variables.
|
|
*/
|
|
object *fn_globals (object *args, object *env) {
|
|
(void) args, (void) env;
|
|
object *result = cons(NULL, NULL);
|
|
object *ptr = result;
|
|
object *arg = GlobalEnv;
|
|
while (arg != NULL) {
|
|
cdr(ptr) = cons(car(car(arg)), NULL); ptr = cdr(ptr);
|
|
arg = cdr(arg);
|
|
}
|
|
return cdr(result);
|
|
}
|
|
|
|
/*
|
|
(locals)
|
|
Returns an association list of local variables and their values.
|
|
*/
|
|
object *fn_locals (object *args, object *env) {
|
|
(void) args;
|
|
return env;
|
|
}
|
|
|
|
/*
|
|
(makunbound symbol)
|
|
Removes the value of the symbol from GlobalEnv and returns the symbol.
|
|
*/
|
|
object *fn_makunbound (object *args, object *env) {
|
|
(void) env;
|
|
object *var = first(args);
|
|
if (!symbolp(var)) error(notasymbol, var);
|
|
delassoc(var, &GlobalEnv);
|
|
return var;
|
|
}
|
|
|
|
/*
|
|
(break)
|
|
Inserts a breakpoint in the program. When evaluated prints Break! and reenters the REPL.
|
|
*/
|
|
object *fn_break (object *args, object *env) {
|
|
(void) args;
|
|
pfstring("\nBreak!\n", pserial);
|
|
BreakLevel++;
|
|
repl(env);
|
|
BreakLevel--;
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(read [stream])
|
|
Reads an atom or list from the serial input and returns it.
|
|
If stream is specified the item is read from the specified stream.
|
|
*/
|
|
object *fn_read (object *args, object *env) {
|
|
(void) env;
|
|
gfun_t gfun = gstreamfun(args);
|
|
return read(gfun);
|
|
}
|
|
|
|
/*
|
|
(prin1 item [stream])
|
|
Prints its argument, and returns its value.
|
|
Strings are printed with quotation marks and escape characters.
|
|
*/
|
|
object *fn_prin1 (object *args, object *env) {
|
|
(void) env;
|
|
object *obj = first(args);
|
|
pfun_t pfun = pstreamfun(cdr(args));
|
|
printobject(obj, pfun);
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
(print item [stream])
|
|
Prints its argument with quotation marks and escape characters, on a new line, and followed by a space.
|
|
If stream is specified the argument is printed to the specified stream.
|
|
*/
|
|
object *fn_print (object *args, object *env) {
|
|
(void) env;
|
|
object *obj = first(args);
|
|
pfun_t pfun = pstreamfun(cdr(args));
|
|
pln(pfun);
|
|
printobject(obj, pfun);
|
|
pfun(' ');
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
(princ item [stream])
|
|
Prints its argument, and returns its value.
|
|
Characters and strings are printed without quotation marks or escape characters.
|
|
*/
|
|
object *fn_princ (object *args, object *env) {
|
|
(void) env;
|
|
object *obj = first(args);
|
|
pfun_t pfun = pstreamfun(cdr(args));
|
|
prin1object(obj, pfun);
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
(terpri [stream])
|
|
Prints a new line, and returns nil.
|
|
If stream is specified the new line is written to the specified stream.
|
|
*/
|
|
object *fn_terpri (object *args, object *env) {
|
|
(void) env;
|
|
pfun_t pfun = pstreamfun(args);
|
|
pln(pfun);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(read-byte stream)
|
|
Reads a byte from a stream and returns it.
|
|
*/
|
|
object *fn_readbyte (object *args, object *env) {
|
|
(void) env;
|
|
gfun_t gfun = gstreamfun(args);
|
|
int c = gfun();
|
|
return (c == -1) ? nil : number(c);
|
|
}
|
|
|
|
/*
|
|
(read-line [stream])
|
|
Reads characters from the serial input up to a newline character, and returns them as a string, excluding the newline.
|
|
If stream is specified the line is read from the specified stream.
|
|
*/
|
|
object *fn_readline (object *args, object *env) {
|
|
(void) env;
|
|
gfun_t gfun = gstreamfun(args);
|
|
return readstring('\n', false, gfun);
|
|
}
|
|
|
|
/*
|
|
(write-byte number [stream])
|
|
Writes a byte to a stream.
|
|
*/
|
|
inline void serialwrite (char c) { Serial.write(c); }
|
|
|
|
object *fn_writebyte (object *args, object *env) {
|
|
(void) env;
|
|
int value = checkinteger(first(args));
|
|
pfun_t pfun = pstreamfun(cdr(args));
|
|
if (pfun == pserial) pfun = serialwrite;
|
|
(pfun)(value);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(write-string string [stream])
|
|
Writes a string. If stream is specified the string is written to the stream.
|
|
*/
|
|
object *fn_writestring (object *args, object *env) {
|
|
(void) env;
|
|
object *obj = first(args);
|
|
pfun_t pfun = pstreamfun(cdr(args));
|
|
flags_t temp = Flags;
|
|
clrflag(PRINTREADABLY);
|
|
printstring(obj, pfun);
|
|
Flags = temp;
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(write-line string [stream])
|
|
Writes a string terminated by a newline character. If stream is specified the string is written to the stream.
|
|
*/
|
|
object *fn_writeline (object *args, object *env) {
|
|
(void) env;
|
|
object *obj = first(args);
|
|
pfun_t pfun = pstreamfun(cdr(args));
|
|
flags_t temp = Flags;
|
|
clrflag(PRINTREADABLY);
|
|
printstring(obj, pfun);
|
|
pln(pfun);
|
|
Flags = temp;
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(restart-i2c stream [read-p])
|
|
Restarts an i2c-stream.
|
|
If read-p is nil or omitted the stream is written to.
|
|
If read-p is an integer it specifies the number of bytes to be read from the stream.
|
|
*/
|
|
object *fn_restarti2c (object *args, object *env) {
|
|
(void) env;
|
|
int stream = isstream(first(args));
|
|
args = cdr(args);
|
|
int read = 0; // Write
|
|
I2Ccount = 0;
|
|
if (args != NULL) {
|
|
object *rw = first(args);
|
|
if (integerp(rw)) I2Ccount = rw->integer;
|
|
read = (rw != NULL);
|
|
}
|
|
int address = stream & 0xFF;
|
|
if (stream>>8 != I2CSTREAM) error2("not an i2c stream");
|
|
TwoWire *port;
|
|
if (address < 128) port = &Wire;
|
|
#if defined(ULISP_I2C1)
|
|
else port = &Wire1;
|
|
#endif
|
|
return I2Crestart(port, address & 0x7F, read) ? tee : nil;
|
|
}
|
|
|
|
/*
|
|
(gc [print time])
|
|
Forces a garbage collection and prints the number of objects collected, and the time taken.
|
|
*/
|
|
object *fn_gc (object *args, object *env) {
|
|
if (args == NULL || first(args) != NULL) {
|
|
int initial = Freespace;
|
|
unsigned long start = micros();
|
|
gc(args, env);
|
|
unsigned long elapsed = micros() - start;
|
|
pfstring("Space: ", pserial);
|
|
pint(Freespace - initial, pserial);
|
|
pfstring(" bytes, Time: ", pserial);
|
|
pint(elapsed, pserial);
|
|
pfstring(" us\n", pserial);
|
|
} else gc(args, env);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(room)
|
|
Returns the number of free Lisp cells remaining.
|
|
*/
|
|
object *fn_room (object *args, object *env) {
|
|
(void) args, (void) env;
|
|
return number(Freespace);
|
|
}
|
|
|
|
/*
|
|
(backtrace [on])
|
|
Sets the state of backtrace according to the boolean flag 'on',
|
|
or with no argument displays the current state of backtrace.
|
|
*/
|
|
object *fn_backtrace (object *args, object *env) {
|
|
(void) env;
|
|
if (args == NULL) return (tstflag(BACKTRACE)) ? tee : nil;
|
|
if (first(args) == NULL) clrflag(BACKTRACE); else setflag(BACKTRACE);
|
|
return first(args);
|
|
}
|
|
|
|
/*
|
|
(save-image [symbol])
|
|
Saves the current uLisp image to non-volatile memory or SD card
|
|
so it can be loaded using load-image.
|
|
*/
|
|
object *
|
|
fn_saveimage(object *args, object *env)
|
|
{
|
|
if (args != NULL) {
|
|
args = eval(first(args), env);
|
|
}
|
|
|
|
return number(saveimage(args));
|
|
}
|
|
|
|
/*
|
|
(load-image [filename])
|
|
Loads a saved uLisp image from non-volatile memory or SD card.
|
|
*/
|
|
object *
|
|
fn_loadimage(object *args, object *env)
|
|
{
|
|
(void) env;
|
|
|
|
if (args != NULL) {
|
|
args = first(args);
|
|
}
|
|
|
|
return number(loadimage(args));
|
|
}
|
|
|
|
/*
|
|
(cls)
|
|
Prints a clear-screen character.
|
|
*/
|
|
object *fn_cls (object *args, object *env) {
|
|
(void) args, (void) env;
|
|
pserial(12);
|
|
return nil;
|
|
}
|
|
|
|
// Arduino procedures
|
|
|
|
/*
|
|
(pinmode pin mode)
|
|
Sets the input/output mode of an Arduino pin number, and returns nil.
|
|
The mode parameter can be an integer, a keyword, or t or nil.
|
|
*/
|
|
object *fn_pinmode (object *args, object *env) {
|
|
(void) env; int pin;
|
|
object *arg = first(args);
|
|
if (keywordp(arg)) pin = checkkeyword(arg);
|
|
else pin = checkinteger(first(args));
|
|
int pm = INPUT;
|
|
arg = second(args);
|
|
if (keywordp(arg)) pm = checkkeyword(arg);
|
|
else if (integerp(arg)) {
|
|
int mode = arg->integer;
|
|
if (mode == 1) pm = OUTPUT; else if (mode == 2) pm = INPUT_PULLUP;
|
|
#if defined(INPUT_PULLDOWN)
|
|
else if (mode == 4) pm = INPUT_PULLDOWN;
|
|
#endif
|
|
} else if (arg != nil) pm = OUTPUT;
|
|
pinMode(pin, pm);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(digitalread pin)
|
|
Reads the state of the specified Arduino pin number and returns t (high) or nil (low).
|
|
*/
|
|
object *fn_digitalread (object *args, object *env) {
|
|
(void) env;
|
|
int pin;
|
|
object *arg = first(args);
|
|
if (keywordp(arg)) pin = checkkeyword(arg);
|
|
else pin = checkinteger(arg);
|
|
if (digitalRead(pin) != 0) return tee; else return nil;
|
|
}
|
|
|
|
/*
|
|
(digitalwrite pin state)
|
|
Sets the state of the specified Arduino pin number.
|
|
*/
|
|
object *fn_digitalwrite (object *args, object *env) {
|
|
(void) env;
|
|
int pin;
|
|
object *arg = first(args);
|
|
if (keywordp(arg)) pin = checkkeyword(arg);
|
|
else pin = checkinteger(arg);
|
|
arg = second(args);
|
|
int mode;
|
|
if (keywordp(arg)) mode = checkkeyword(arg);
|
|
else if (integerp(arg)) mode = arg->integer ? HIGH : LOW;
|
|
else mode = (arg != nil) ? HIGH : LOW;
|
|
digitalWrite(pin, mode);
|
|
return arg;
|
|
}
|
|
|
|
/*
|
|
(analogread pin)
|
|
Reads the specified Arduino analogue pin number and returns the value.
|
|
*/
|
|
object *fn_analogread (object *args, object *env) {
|
|
(void) env;
|
|
int pin;
|
|
object *arg = first(args);
|
|
if (keywordp(arg)) pin = checkkeyword(arg);
|
|
else {
|
|
pin = checkinteger(arg);
|
|
checkanalogread(pin);
|
|
}
|
|
return number(analogRead(pin));
|
|
}
|
|
|
|
/*
|
|
(analogreference keyword)
|
|
Specifies a keyword to set the analogue reference voltage used for analogue input.
|
|
*/
|
|
object *fn_analogreference (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
#if defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(MAX32620) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \
|
|
|| defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) \
|
|
|| defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2040) \
|
|
|| defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) \
|
|
|| defined(ARDUINO_PIMORONI_PICO_PLUS_2) || defined(ARDUINO_PIMORONI_TINY2350) \
|
|
|| defined(ARDUINO_NANO_MATTER)
|
|
error2("not supported");
|
|
#else
|
|
analogReference((eAnalogReference)checkkeyword(arg));
|
|
#endif
|
|
return arg;
|
|
}
|
|
|
|
/*
|
|
(analogreadresolution bits)
|
|
Specifies the resolution for the analogue inputs on platforms that support it.
|
|
The default resolution on all platforms is 10 bits.
|
|
*/
|
|
object *fn_analogreadresolution (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
#if defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_2) \
|
|
|| defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) \
|
|
|| defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER)
|
|
error2("not supported");
|
|
#else
|
|
analogReadResolution(checkinteger(arg));
|
|
#endif
|
|
return arg;
|
|
}
|
|
|
|
/*
|
|
(analogwrite pin value)
|
|
Writes the value to the specified Arduino pin number.
|
|
*/
|
|
object *fn_analogwrite (object *args, object *env) {
|
|
(void) env;
|
|
int pin;
|
|
object *arg = first(args);
|
|
if (keywordp(arg)) pin = checkkeyword(arg);
|
|
else pin = checkinteger(arg);
|
|
checkanalogwrite(pin);
|
|
object *value = second(args);
|
|
analogWrite(pin, checkinteger(value));
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
(analogwrite pin value)
|
|
Sets the analogue write resolution.
|
|
*/
|
|
object *fn_analogwriteresolution (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
analogWriteResolution(checkinteger(arg));
|
|
return arg;
|
|
}
|
|
|
|
/*
|
|
(delay number)
|
|
Delays for a specified number of milliseconds.
|
|
*/
|
|
object *fn_delay (object *args, object *env) {
|
|
(void) env;
|
|
object *arg1 = first(args);
|
|
unsigned long start = millis();
|
|
unsigned long total = checkinteger(arg1);
|
|
do testescape();
|
|
while (millis() - start < total);
|
|
return arg1;
|
|
}
|
|
|
|
/*
|
|
(millis)
|
|
Returns the time in milliseconds that uLisp has been running.
|
|
*/
|
|
object *fn_millis (object *args, object *env) {
|
|
(void) args, (void) env;
|
|
return number(millis());
|
|
}
|
|
|
|
/*
|
|
(sleep secs)
|
|
Puts the processor into a low-power sleep mode for secs.
|
|
Only supported on some platforms. On other platforms it does delay(1000*secs).
|
|
*/
|
|
object *fn_sleep (object *args, object *env) {
|
|
(void) env;
|
|
object *arg1 = first(args);
|
|
doze(checkinteger(arg1));
|
|
return arg1;
|
|
}
|
|
|
|
/*
|
|
(note [pin] [note] [octave])
|
|
Generates a square wave on pin.
|
|
note represents the note in the well-tempered scale.
|
|
The argument octave can specify an octave; default 0.
|
|
*/
|
|
object *fn_note (object *args, object *env) {
|
|
(void) env;
|
|
static int pin = 255;
|
|
if (args != NULL) {
|
|
pin = checkinteger(first(args));
|
|
int note = 48, octave = 0;
|
|
if (cdr(args) != NULL) {
|
|
note = checkinteger(second(args));
|
|
if (cddr(args) != NULL) octave = checkinteger(third(args));
|
|
}
|
|
playnote(pin, note, octave);
|
|
} else nonote(pin);
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(register address [value])
|
|
Reads or writes the value of a peripheral register.
|
|
If value is not specified the function returns the value of the register at address.
|
|
If value is specified the value is written to the register at address and the function returns value.
|
|
*/
|
|
object *fn_register (object *args, object *env) {
|
|
(void) env;
|
|
object *arg = first(args);
|
|
int addr;
|
|
if (keywordp(arg)) addr = checkkeyword(arg);
|
|
else addr = checkinteger(first(args));
|
|
if (cdr(args) == NULL) return number(*(uint32_t *)addr);
|
|
(*(uint32_t *)addr) = checkinteger(second(args));
|
|
return second(args);
|
|
}
|
|
|
|
// Tree Editor
|
|
|
|
/*
|
|
(edit 'function)
|
|
Calls the Lisp tree editor to allow you to edit a function definition.
|
|
*/
|
|
object *fn_edit (object *args, object *env) {
|
|
object *fun = first(args);
|
|
object *pair = findvalue(fun, env);
|
|
clrflag(EXITEDITOR);
|
|
object *arg = edit(eval(fun, env));
|
|
cdr(pair) = arg;
|
|
return arg;
|
|
}
|
|
|
|
// Pretty printer
|
|
|
|
/*
|
|
(pprint item [str])
|
|
Prints its argument, using the pretty printer, to display it formatted in a structured way.
|
|
If str is specified it prints to the specified stream. It returns no value.
|
|
*/
|
|
object *fn_pprint (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
|
|
pln(pfun);
|
|
superprint(obj, 0, pfun);
|
|
ppwidth = PPWIDTH;
|
|
return bsymbol(NOTHING);
|
|
}
|
|
|
|
/*
|
|
(pprintall [str])
|
|
Pretty-prints the definition of every function and variable defined in the uLisp workspace.
|
|
If str is specified it prints to the specified stream. It returns no value.
|
|
*/
|
|
object *fn_pprintall (object *args, object *env) {
|
|
(void) env;
|
|
pfun_t pfun = pstreamfun(args);
|
|
#if defined(gfxsupport)
|
|
if (pfun == gfxwrite) ppwidth = GFXPPWIDTH;
|
|
#endif
|
|
object *globals = GlobalEnv;
|
|
while (globals != NULL) {
|
|
object *pair = first(globals);
|
|
object *var = car(pair);
|
|
object *val = cdr(pair);
|
|
pln(pfun);
|
|
if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) {
|
|
superprint(cons(bsymbol(DEFUN), cons(var, cdr(val))), 0, pfun);
|
|
} else if (consp(val) && car(val)->type == CODE) {
|
|
superprint(cons(bsymbol(DEFCODE), cons(var, cdr(val))), 0, pfun);
|
|
} else {
|
|
superprint(cons(bsymbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun);
|
|
}
|
|
pln(pfun);
|
|
testescape();
|
|
globals = cdr(globals);
|
|
}
|
|
ppwidth = PPWIDTH;
|
|
return bsymbol(NOTHING);
|
|
}
|
|
|
|
// Format
|
|
|
|
/*
|
|
(format output controlstring [arguments]*)
|
|
Outputs its arguments formatted according to the format directives in controlstring.
|
|
*/
|
|
object *fn_format (object *args, object *env) {
|
|
(void) env;
|
|
pfun_t pfun = pserial;
|
|
object *output = first(args);
|
|
object *obj;
|
|
if (output == nil) { obj = startstring(); pfun = pstr; }
|
|
else if (!eq(output, tee)) pfun = pstreamfun(args);
|
|
object *formatstr = checkstring(second(args));
|
|
object *save = NULL;
|
|
args = cddr(args);
|
|
int len = stringlength(formatstr);
|
|
uint16_t n = 0, width = 0, w, bra = 0;
|
|
char pad = ' ';
|
|
bool tilde = false, mute = false, comma = false, quote = false;
|
|
while (n < len) {
|
|
char ch = nthchar(formatstr, n);
|
|
char ch2 = ch & ~0x20; // force to upper case
|
|
if (tilde) {
|
|
if (ch == '}') {
|
|
if (save == NULL) formaterr(formatstr, "no matching ~{", n);
|
|
if (args == NULL) { args = cdr(save); save = NULL; } else n = bra;
|
|
mute = false; tilde = false;
|
|
}
|
|
else if (!mute) {
|
|
if (comma && quote) { pad = ch; comma = false, quote = false; }
|
|
else if (ch == '\'') {
|
|
if (comma) quote = true;
|
|
else formaterr(formatstr, "quote not valid", n);
|
|
}
|
|
else if (ch == '~') { pfun('~'); tilde = false; }
|
|
else if (ch >= '0' && ch <= '9') width = width*10 + ch - '0';
|
|
else if (ch == ',') comma = true;
|
|
else if (ch == '%') { pln(pfun); tilde = false; }
|
|
else if (ch == '&') { pfl(pfun); tilde = false; }
|
|
else if (ch == '^') {
|
|
if (save != NULL && args == NULL) mute = true;
|
|
tilde = false;
|
|
}
|
|
else if (ch == '{') {
|
|
if (save != NULL) formaterr(formatstr, "can't nest ~{", n);
|
|
if (args == NULL) formaterr(formatstr, noargument, n);
|
|
if (!listp(first(args))) formaterr(formatstr, notalist, n);
|
|
save = args; args = first(args); bra = n; tilde = false;
|
|
if (args == NULL) mute = true;
|
|
}
|
|
else if (ch2 == 'A' || ch2 == 'S' || ch2 == 'D' || ch2 == 'G' || ch2 == 'X' || ch2 == 'B') {
|
|
if (args == NULL) formaterr(formatstr, noargument, n);
|
|
object *arg = first(args); args = cdr(args);
|
|
uint8_t aw = atomwidth(arg);
|
|
if (width < aw) w = 0; else w = width-aw;
|
|
tilde = false;
|
|
if (ch2 == 'A') { prin1object(arg, pfun); indent(w, pad, pfun); }
|
|
else if (ch2 == 'S') { printobject(arg, pfun); indent(w, pad, pfun); }
|
|
else if (ch2 == 'D' || ch2 == 'G') { indent(w, pad, pfun); prin1object(arg, pfun); }
|
|
else if (ch2 == 'X' || ch2 == 'B') {
|
|
if (integerp(arg)) {
|
|
uint8_t base = (ch2 == 'B') ? 2 : 16;
|
|
uint8_t hw = basewidth(arg, base); if (width < hw) w = 0; else w = width-hw;
|
|
indent(w, pad, pfun); pintbase(arg->integer, base, pfun);
|
|
} else {
|
|
indent(w, pad, pfun); prin1object(arg, pfun);
|
|
}
|
|
}
|
|
tilde = false;
|
|
} else formaterr(formatstr, "invalid directive", n);
|
|
}
|
|
} else {
|
|
if (ch == '~') { tilde = true; pad = ' '; width = 0; comma = false; quote = false; }
|
|
else if (!mute) pfun(ch);
|
|
}
|
|
n++;
|
|
}
|
|
if (output == nil) return obj;
|
|
else return nil;
|
|
}
|
|
|
|
// LispLibrary
|
|
|
|
/*
|
|
(require 'symbol)
|
|
Loads the definition of a function defined with defun, or a variable defined with defvar, from the Lisp Library.
|
|
It returns t if it was loaded, or nil if the symbol is already defined or isn't defined in the Lisp Library.
|
|
*/
|
|
object *fn_require (object *args, object *env) {
|
|
object *arg = first(args);
|
|
object *globals = GlobalEnv;
|
|
if (!symbolp(arg)) error(notasymbol, arg);
|
|
while (globals != NULL) {
|
|
object *pair = first(globals);
|
|
object *var = car(pair);
|
|
if (symbolp(var) && var == arg) return nil;
|
|
globals = cdr(globals);
|
|
}
|
|
GlobalStringIndex = 0;
|
|
object *line = read(glibrary);
|
|
while (line != NULL) {
|
|
// Is this the definition we want
|
|
symbol_t fname = first(line)->name;
|
|
if ((fname == sym(DEFUN) || fname == sym(DEFVAR)) && symbolp(second(line)) && second(line)->name == arg->name) {
|
|
eval(line, env);
|
|
return tee;
|
|
}
|
|
line = read(glibrary);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(list-library)
|
|
Prints a list of the functions defined in the List Library.
|
|
*/
|
|
object *fn_listlibrary (object *args, object *env) {
|
|
(void) args, (void) env;
|
|
GlobalStringIndex = 0;
|
|
object *line = read(glibrary);
|
|
while (line != NULL) {
|
|
builtin_t bname = builtin(first(line)->name);
|
|
if (bname == DEFUN || bname == DEFVAR) {
|
|
printsymbol(second(line), pserial); pserial(' ');
|
|
}
|
|
line = read(glibrary);
|
|
}
|
|
return bsymbol(NOTHING);
|
|
}
|
|
|
|
// Documentation
|
|
|
|
/*
|
|
(? item)
|
|
Prints the documentation string of a built-in or user-defined function.
|
|
*/
|
|
object *sp_help (object *args, object *env) {
|
|
if (args == NULL) error2(noargument);
|
|
object *docstring = documentation(first(args), env);
|
|
if (docstring) {
|
|
flags_t temp = Flags;
|
|
clrflag(PRINTREADABLY);
|
|
printstring(docstring, pserial);
|
|
Flags = temp;
|
|
}
|
|
return bsymbol(NOTHING);
|
|
}
|
|
|
|
/*
|
|
(documentation 'symbol [type])
|
|
Returns the documentation string of a built-in or user-defined function. The type argument is ignored.
|
|
*/
|
|
object *fn_documentation (object *args, object *env) {
|
|
return documentation(first(args), env);
|
|
}
|
|
|
|
/*
|
|
(apropos item)
|
|
Prints the user-defined and built-in functions whose names contain the specified string or symbol.
|
|
*/
|
|
object *fn_apropos (object *args, object *env) {
|
|
(void) env;
|
|
apropos(first(args), true);
|
|
return bsymbol(NOTHING);
|
|
}
|
|
|
|
/*
|
|
(apropos-list item)
|
|
Returns a list of user-defined and built-in functions whose names contain the specified string or symbol.
|
|
*/
|
|
object *fn_aproposlist (object *args, object *env) {
|
|
(void) env;
|
|
return apropos(first(args), false);
|
|
}
|
|
|
|
// Error handling
|
|
|
|
/*
|
|
(unwind-protect form1 [forms]*)
|
|
Evaluates form1 and forms in order and returns the value of form1,
|
|
but guarantees to evaluate forms even if an error occurs in form1.
|
|
*/
|
|
object *sp_unwindprotect (object *args, object *env) {
|
|
if (args == NULL) error2(toofewargs);
|
|
object *current_GCStack = GCStack;
|
|
jmp_buf dynamic_handler;
|
|
jmp_buf *previous_handler = handler;
|
|
handler = &dynamic_handler;
|
|
object *protected_form = first(args);
|
|
object *result;
|
|
|
|
bool signaled = false;
|
|
if (!setjmp(dynamic_handler)) {
|
|
result = eval(protected_form, env);
|
|
} else {
|
|
GCStack = current_GCStack;
|
|
signaled = true;
|
|
}
|
|
handler = previous_handler;
|
|
|
|
object *protective_forms = cdr(args);
|
|
while (protective_forms != NULL) {
|
|
eval(car(protective_forms), env);
|
|
if (tstflag(RETURNFLAG)) break;
|
|
protective_forms = cdr(protective_forms);
|
|
}
|
|
|
|
if (!signaled) return result;
|
|
GCStack = NULL;
|
|
longjmp(*handler, 1);
|
|
}
|
|
|
|
/*
|
|
(ignore-errors [forms]*)
|
|
Evaluates forms ignoring errors.
|
|
*/
|
|
object *sp_ignoreerrors (object *args, object *env) {
|
|
object *current_GCStack = GCStack;
|
|
jmp_buf dynamic_handler;
|
|
jmp_buf *previous_handler = handler;
|
|
handler = &dynamic_handler;
|
|
object *result = nil;
|
|
|
|
bool muffled = tstflag(MUFFLEERRORS);
|
|
setflag(MUFFLEERRORS);
|
|
bool signaled = false;
|
|
if (!setjmp(dynamic_handler)) {
|
|
while (args != NULL) {
|
|
result = eval(car(args), env);
|
|
if (tstflag(RETURNFLAG)) break;
|
|
args = cdr(args);
|
|
}
|
|
} else {
|
|
GCStack = current_GCStack;
|
|
signaled = true;
|
|
}
|
|
handler = previous_handler;
|
|
if (!muffled) clrflag(MUFFLEERRORS);
|
|
|
|
if (signaled) return bsymbol(NOTHING);
|
|
else return result;
|
|
}
|
|
|
|
/*
|
|
(error controlstring [arguments]*)
|
|
Signals an error. The message is printed by format using the controlstring and arguments.
|
|
*/
|
|
object *sp_error (object *args, object *env) {
|
|
object *message = eval(cons(bsymbol(FORMAT), cons(nil, args)), env);
|
|
if (!tstflag(MUFFLEERRORS)) {
|
|
flags_t temp = Flags;
|
|
clrflag(PRINTREADABLY);
|
|
pfstring("Error: ", pserial); printstring(message, pserial);
|
|
Flags = temp;
|
|
pln(pserial);
|
|
}
|
|
GCStack = NULL;
|
|
longjmp(*handler, 1);
|
|
}
|
|
|
|
// SD Card utilities
|
|
|
|
/*
|
|
(directory)
|
|
Returns a list of the filenames of the files on the SD card.
|
|
*/
|
|
object *fn_directory (object *args, object *env) {
|
|
(void) args, (void) env;
|
|
#if defined(sdcardsupport)
|
|
SDBegin();
|
|
File root = SD.open("/");
|
|
if (!root) error2("problem reading from SD card");
|
|
object *result = cons(NULL, NULL);
|
|
object *ptr = result;
|
|
while (true) {
|
|
File entry = root.openNextFile();
|
|
if (!entry) break;
|
|
object *filename = lispstring((char*)entry.name());
|
|
cdr(ptr) = cons(filename, NULL);
|
|
ptr = cdr(ptr);
|
|
entry.close();
|
|
}
|
|
root.close();
|
|
return cdr(result);
|
|
#else
|
|
error2("not supported");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
// Wi-Fi
|
|
|
|
/*
|
|
(with-client (str [address port]) form*)
|
|
Evaluates the forms with str bound to a wifi-stream.
|
|
*/
|
|
object *sp_withclient (object *args, object *env) {
|
|
#if defined(ULISP_WIFI)
|
|
object *params = checkarguments(args, 1, 3);
|
|
object *var = first(params);
|
|
char buffer[BUFFERSIZE];
|
|
params = cdr(params);
|
|
int n;
|
|
if (params == NULL) {
|
|
client = server.accept();
|
|
if (!client) return nil;
|
|
n = 2;
|
|
} else {
|
|
object *address = eval(first(params), env);
|
|
object *port = eval(second(params), env);
|
|
int success;
|
|
if (stringp(address)) success = client.connect(cstring(address, buffer, BUFFERSIZE), checkinteger(port));
|
|
else if (integerp(address)) success = client.connect(address->integer, checkinteger(port));
|
|
else error2("invalid address");
|
|
if (!success) return nil;
|
|
n = 1;
|
|
}
|
|
object *pair = cons(var, stream(WIFISTREAM, n));
|
|
push(pair,env);
|
|
object *forms = cdr(args);
|
|
object *result = eval(tf_progn(forms,env), env);
|
|
client.stop();
|
|
return result;
|
|
#else
|
|
(void) args, (void) env;
|
|
error2("not supported");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
(available stream)
|
|
Returns the number of bytes available for reading from the wifi-stream, or zero if no bytes are available.
|
|
*/
|
|
object *fn_available (object *args, object *env) {
|
|
#if defined (ULISP_WIFI)
|
|
(void) env;
|
|
if (isstream(first(args))>>8 != WIFISTREAM) error2("invalid stream");
|
|
return number(client.available());
|
|
#else
|
|
(void) args, (void) env;
|
|
error2("not supported");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
(wifi-server)
|
|
Starts a Wi-Fi server running. It returns nil.
|
|
*/
|
|
object *fn_wifiserver (object *args, object *env) {
|
|
#if defined (ULISP_WIFI)
|
|
(void) args, (void) env;
|
|
server.begin();
|
|
return nil;
|
|
#else
|
|
(void) args, (void) env;
|
|
error2("not supported");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
(wifi-softap ssid [password channel hidden])
|
|
Set up a soft access point to establish a Wi-Fi network.
|
|
Returns the IP address as a string or nil if unsuccessful.
|
|
*/
|
|
object *fn_wifisoftap (object *args, object *env) {
|
|
#if defined (ULISP_WIFI)
|
|
(void) env;
|
|
char ssid[33], pass[65];
|
|
object *first = first(args); args = cdr(args);
|
|
if (args == NULL) WiFi.beginAP(cstring(first, ssid, 33));
|
|
else {
|
|
object *second = first(args);
|
|
args = cdr(args);
|
|
int channel = 1;
|
|
if (args != NULL) {
|
|
channel = checkinteger(first(args));
|
|
args = cdr(args);
|
|
}
|
|
WiFi.beginAP(cstring(first, ssid, 33), cstring(second, pass, 65), channel);
|
|
}
|
|
return iptostring(WiFi.localIP());
|
|
#else
|
|
(void) args, (void) env;
|
|
error2("not supported");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
(connected stream)
|
|
Returns t or nil to indicate if the client on stream is connected.
|
|
*/
|
|
object *fn_connected (object *args, object *env) {
|
|
#if defined (ULISP_WIFI)
|
|
(void) env;
|
|
if (isstream(first(args))>>8 != WIFISTREAM) error2("invalid stream");
|
|
return client.connected() ? tee : nil;
|
|
#else
|
|
(void) args, (void) env;
|
|
error2("not supported");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
(wifi-localip)
|
|
Returns the IP address of the local network as a string.
|
|
*/
|
|
object *fn_wifilocalip (object *args, object *env) {
|
|
#if defined (ULISP_WIFI)
|
|
(void) args, (void) env;
|
|
return iptostring(WiFi.localIP());
|
|
#else
|
|
(void) args, (void) env;
|
|
error2("not supported");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
(wifi-connect [ssid pass])
|
|
Connects to the Wi-Fi network ssid using password pass. It returns the IP address as a string.
|
|
*/
|
|
object *fn_wificonnect (object *args, object *env) {
|
|
#if defined (ULISP_WIFI)
|
|
(void) env;
|
|
char ssid[33], pass[65];
|
|
int result = 0;
|
|
if (args == NULL) { WiFi.disconnect(); return nil; }
|
|
if (cdr(args) == NULL) WiFi.begin(cstring(first(args), ssid, 33));
|
|
else {
|
|
if (cddr(args) != NULL) WiFi.config(ipstring(third(args)));
|
|
result = WiFi.begin(cstring(first(args), ssid, 33), cstring(second(args), pass, 65));
|
|
}
|
|
if (result == WL_CONNECTED) return iptostring(WiFi.localIP());
|
|
else if (result == WL_NO_SSID_AVAIL) error2("network not found");
|
|
else if (result == WL_CONNECT_FAILED) error2("connection failed");
|
|
else error2("unable to connect");
|
|
return nil;
|
|
#else
|
|
(void) args, (void) env;
|
|
error2("not supported");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
// Graphics functions
|
|
|
|
/*
|
|
(with-gfx (str) form*)
|
|
Evaluates the forms with str bound to an gfx-stream so you can print text
|
|
to the graphics display using the standard uLisp print commands.
|
|
*/
|
|
object *sp_withgfx (object *args, object *env) {
|
|
#if defined(gfxsupport)
|
|
object *params = checkarguments(args, 1, 1);
|
|
object *var = first(params);
|
|
object *pair = cons(var, stream(GFXSTREAM, 1));
|
|
push(pair,env);
|
|
object *forms = cdr(args);
|
|
object *result = eval(tf_progn(forms,env), env);
|
|
return result;
|
|
#else
|
|
(void) args, (void) env;
|
|
error2("not supported");
|
|
return nil;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
(draw-pixel x y [colour])
|
|
Draws a pixel at coordinates (x,y) in colour, or white if omitted.
|
|
*/
|
|
object *fn_drawpixel (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t colour = TEXT_COLOR;
|
|
if (cddr(args) != NULL) colour = checkinteger(third(args));
|
|
tft.drawPixel(checkinteger(first(args)), checkinteger(second(args)), colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(draw-line x0 y0 x1 y1 [colour])
|
|
Draws a line from (x0,y0) to (x1,y1) in colour, or white if omitted.
|
|
*/
|
|
object *fn_drawline (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t params[4], colour = TEXT_COLOR;
|
|
for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); }
|
|
if (args != NULL) colour = checkinteger(car(args));
|
|
tft.drawLine(params[0], params[1], params[2], params[3], colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(draw-rect x y w h [colour])
|
|
Draws an outline rectangle with its top left corner at (x,y), with width w,
|
|
and with height h. The outline is drawn in colour, or white if omitted.
|
|
*/
|
|
object *fn_drawrect (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t params[4], colour = TEXT_COLOR;
|
|
for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); }
|
|
if (args != NULL) colour = checkinteger(car(args));
|
|
tft.drawRect(params[0], params[1], params[2], params[3], colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(fill-rect x y w h [colour])
|
|
Draws a filled rectangle with its top left corner at (x,y), with width w,
|
|
and with height h. The outline is drawn in colour, or white if omitted.
|
|
*/
|
|
object *fn_fillrect (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t params[4], colour = TEXT_COLOR;
|
|
for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); }
|
|
if (args != NULL) colour = checkinteger(car(args));
|
|
tft.fillRect(params[0], params[1], params[2], params[3], colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(draw-circle x y r [colour])
|
|
Draws an outline circle with its centre at (x, y) and with radius r.
|
|
The circle is drawn in colour, or white if omitted.
|
|
*/
|
|
object *fn_drawcircle (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t params[3], colour = TEXT_COLOR;
|
|
for (int i=0; i<3; i++) { params[i] = checkinteger(car(args)); args = cdr(args); }
|
|
if (args != NULL) colour = checkinteger(car(args));
|
|
tft.drawCircle(params[0], params[1], params[2], colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(fill-circle x y r [colour])
|
|
Draws a filled circle with its centre at (x, y) and with radius r.
|
|
The circle is drawn in colour, or white if omitted.
|
|
*/
|
|
object *fn_fillcircle (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t params[3], colour = TEXT_COLOR;
|
|
for (int i=0; i<3; i++) { params[i] = checkinteger(car(args)); args = cdr(args); }
|
|
if (args != NULL) colour = checkinteger(car(args));
|
|
tft.fillCircle(params[0], params[1], params[2], colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(draw-round-rect x y w h radius [colour])
|
|
Draws an outline rounded rectangle with its top left corner at (x,y), with width w,
|
|
height h, and corner radius radius. The outline is drawn in colour, or white if omitted.
|
|
*/
|
|
object *fn_drawroundrect (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t params[5], colour = TEXT_COLOR;
|
|
for (int i=0; i<5; i++) { params[i] = checkinteger(car(args)); args = cdr(args); }
|
|
if (args != NULL) colour = checkinteger(car(args));
|
|
tft.drawRoundRect(params[0], params[1], params[2], params[3], params[4], colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(fill-round-rect x y w h radius [colour])
|
|
Draws a filled rounded rectangle with its top left corner at (x,y), with width w,
|
|
height h, and corner radius radius. The outline is drawn in colour, or white if omitted.
|
|
*/
|
|
object *fn_fillroundrect (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t params[5], colour = TEXT_COLOR;
|
|
for (int i=0; i<5; i++) { params[i] = checkinteger(car(args)); args = cdr(args); }
|
|
if (args != NULL) colour = checkinteger(car(args));
|
|
tft.fillRoundRect(params[0], params[1], params[2], params[3], params[4], colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(draw-triangle x0 y0 x1 y1 x2 y2 [colour])
|
|
Draws an outline triangle between (x1,y1), (x2,y2), and (x3,y3).
|
|
The outline is drawn in colour, or white if omitted.
|
|
*/
|
|
object *fn_drawtriangle (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t params[6], colour = TEXT_COLOR;
|
|
for (int i=0; i<6; i++) { params[i] = checkinteger(car(args)); args = cdr(args); }
|
|
if (args != NULL) colour = checkinteger(car(args));
|
|
tft.drawTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(fill-triangle x0 y0 x1 y1 x2 y2 [colour])
|
|
Draws a filled triangle between (x1,y1), (x2,y2), and (x3,y3).
|
|
The outline is drawn in colour, or white if omitted.
|
|
*/
|
|
object *fn_filltriangle (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t params[6], colour = TEXT_COLOR;
|
|
for (int i=0; i<6; i++) { params[i] = checkinteger(car(args)); args = cdr(args); }
|
|
if (args != NULL) colour = checkinteger(car(args));
|
|
tft.fillTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(draw-char x y char [colour background size])
|
|
Draws the character char with its top left corner at (x,y).
|
|
The character is drawn in a 5 x 7 pixel font in colour against background,
|
|
which default to white and black respectively.
|
|
The character can optionally be scaled by size.
|
|
*/
|
|
object *fn_drawchar (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t colour = TEXT_COLOR, bg = BG_COLOR, size = 1;
|
|
object *more = cdr(cddr(args));
|
|
if (more != NULL) {
|
|
colour = checkinteger(car(more));
|
|
more = cdr(more);
|
|
if (more != NULL) {
|
|
bg = checkinteger(car(more));
|
|
more = cdr(more);
|
|
if (more != NULL) size = checkinteger(car(more));
|
|
}
|
|
}
|
|
tft.drawChar(checkinteger(first(args)), checkinteger(second(args)), checkchar(third(args)),
|
|
colour, bg, size);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(set-cursor x y)
|
|
Sets the start point for text plotting to (x, y).
|
|
*/
|
|
object *fn_setcursor (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
tft.setCursor(checkinteger(first(args)), checkinteger(second(args)));
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(set-text-color colour [background])
|
|
Sets the text colour for text plotted using (with-gfx ...).
|
|
*/
|
|
object *fn_settextcolor (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
if (cdr(args) != NULL) tft.setTextColor(checkinteger(first(args)), checkinteger(second(args)));
|
|
else tft.setTextColor(checkinteger(first(args)));
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(set-text-size scale)
|
|
Scales text by the specified size, default 1.
|
|
*/
|
|
object *fn_settextsize (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
tft.setTextSize(checkinteger(first(args)));
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(set-text-wrap boolean)
|
|
Specified whether text wraps at the right-hand edge of the display; the default is t.
|
|
*/
|
|
object *fn_settextwrap (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
tft.setTextWrap(first(args) != NULL);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(fill-screen [colour])
|
|
Fills or clears the screen with colour, default black.
|
|
*/
|
|
object *fn_fillscreen (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
uint16_t colour = COLOR_BLACK;
|
|
if (args != NULL) colour = checkinteger(first(args));
|
|
tft.fillScreen(colour);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(set-rotation option)
|
|
Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3.
|
|
*/
|
|
object *fn_setrotation (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
tft.setRotation(checkinteger(first(args)));
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
(invert-display boolean)
|
|
Mirror-images the display.
|
|
*/
|
|
object *fn_invertdisplay (object *args, object *env) {
|
|
(void) env;
|
|
#if defined(gfxsupport)
|
|
tft.invertDisplay(first(args) != NULL);
|
|
#else
|
|
(void) args;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
// Built-in symbol names
|
|
const char string0[] = "nil";
|
|
const char string1[] = "t";
|
|
const char string2[] = "nothing";
|
|
const char string3[] = "&optional";
|
|
const char string4[] = "*features*";
|
|
const char string5[] = ":initial-element";
|
|
const char string6[] = ":element-type";
|
|
const char string7[] = ":test";
|
|
const char string8[] = ":a";
|
|
const char string9[] = ":b";
|
|
const char string10[] = ":c";
|
|
const char string11[] = "bit";
|
|
const char string12[] = "&rest";
|
|
const char string13[] = "lambda";
|
|
const char string14[] = "let";
|
|
const char string15[] = "let*";
|
|
const char string16[] = "closure";
|
|
const char string17[] = "*pc*";
|
|
const char string18[] = "quote";
|
|
const char string19[] = "defun";
|
|
const char string20[] = "defvar";
|
|
const char string21[] = "defcode";
|
|
const char string22[] = "eq";
|
|
const char string23[] = "car";
|
|
const char string24[] = "first";
|
|
const char string25[] = "cdr";
|
|
const char string26[] = "rest";
|
|
const char string27[] = "nth";
|
|
const char string28[] = "aref";
|
|
const char string29[] = "char";
|
|
const char string30[] = "string";
|
|
const char string31[] = "pinmode";
|
|
const char string32[] = "digitalwrite";
|
|
const char string33[] = "analogread";
|
|
const char string34[] = "analogreference";
|
|
const char string35[] = "register";
|
|
const char string36[] = "format";
|
|
const char string37[] = "or";
|
|
const char string38[] = "setq";
|
|
const char string39[] = "loop";
|
|
const char string40[] = "push";
|
|
const char string41[] = "pop";
|
|
const char string42[] = "incf";
|
|
const char string43[] = "decf";
|
|
const char string44[] = "setf";
|
|
const char string45[] = "dolist";
|
|
const char string46[] = "dotimes";
|
|
const char string47[] = "do";
|
|
const char string48[] = "do*";
|
|
const char string49[] = "trace";
|
|
const char string50[] = "untrace";
|
|
const char string51[] = "for-millis";
|
|
const char string52[] = "time";
|
|
const char string53[] = "with-output-to-string";
|
|
const char string54[] = "with-serial";
|
|
const char string55[] = "with-i2c";
|
|
const char string56[] = "with-spi";
|
|
const char string57[] = "with-sd-card";
|
|
const char string58[] = "progn";
|
|
const char string59[] = "if";
|
|
const char string60[] = "cond";
|
|
const char string61[] = "when";
|
|
const char string62[] = "unless";
|
|
const char string63[] = "case";
|
|
const char string64[] = "and";
|
|
const char string65[] = "not";
|
|
const char string66[] = "null";
|
|
const char string67[] = "cons";
|
|
const char string68[] = "atom";
|
|
const char string69[] = "listp";
|
|
const char string70[] = "consp";
|
|
const char string71[] = "symbolp";
|
|
const char string72[] = "arrayp";
|
|
const char string73[] = "boundp";
|
|
const char string74[] = "keywordp";
|
|
const char string75[] = "set";
|
|
const char string76[] = "streamp";
|
|
const char string77[] = "equal";
|
|
const char string78[] = "caar";
|
|
const char string79[] = "cadr";
|
|
const char string80[] = "second";
|
|
const char string81[] = "cdar";
|
|
const char string82[] = "cddr";
|
|
const char string83[] = "caaar";
|
|
const char string84[] = "caadr";
|
|
const char string85[] = "cadar";
|
|
const char string86[] = "caddr";
|
|
const char string87[] = "third";
|
|
const char string88[] = "cdaar";
|
|
const char string89[] = "cdadr";
|
|
const char string90[] = "cddar";
|
|
const char string91[] = "cdddr";
|
|
const char string92[] = "length";
|
|
const char string93[] = "array-dimensions";
|
|
const char string94[] = "list";
|
|
const char string95[] = "copy-list";
|
|
const char string96[] = "make-array";
|
|
const char string97[] = "reverse";
|
|
const char string98[] = "assoc";
|
|
const char string99[] = "member";
|
|
const char string100[] = "apply";
|
|
const char string101[] = "funcall";
|
|
const char string102[] = "append";
|
|
const char string103[] = "mapc";
|
|
const char string104[] = "mapl";
|
|
const char string105[] = "mapcar";
|
|
const char string106[] = "mapcan";
|
|
const char string107[] = "maplist";
|
|
const char string108[] = "mapcon";
|
|
const char string109[] = "+";
|
|
const char string110[] = "-";
|
|
const char string111[] = "*";
|
|
const char string112[] = "/";
|
|
const char string113[] = "mod";
|
|
const char string114[] = "rem";
|
|
const char string115[] = "1+";
|
|
const char string116[] = "1-";
|
|
const char string117[] = "abs";
|
|
const char string118[] = "random";
|
|
const char string119[] = "max";
|
|
const char string120[] = "min";
|
|
const char string121[] = "/=";
|
|
const char string122[] = "=";
|
|
const char string123[] = "<";
|
|
const char string124[] = "<=";
|
|
const char string125[] = ">";
|
|
const char string126[] = ">=";
|
|
const char string127[] = "plusp";
|
|
const char string128[] = "minusp";
|
|
const char string129[] = "zerop";
|
|
const char string130[] = "oddp";
|
|
const char string131[] = "evenp";
|
|
const char string132[] = "integerp";
|
|
const char string133[] = "numberp";
|
|
const char string134[] = "float";
|
|
const char string135[] = "floatp";
|
|
const char string136[] = "sin";
|
|
const char string137[] = "cos";
|
|
const char string138[] = "tan";
|
|
const char string139[] = "asin";
|
|
const char string140[] = "acos";
|
|
const char string141[] = "atan";
|
|
const char string142[] = "sinh";
|
|
const char string143[] = "cosh";
|
|
const char string144[] = "tanh";
|
|
const char string145[] = "exp";
|
|
const char string146[] = "sqrt";
|
|
const char string147[] = "log";
|
|
const char string148[] = "expt";
|
|
const char string149[] = "ceiling";
|
|
const char string150[] = "floor";
|
|
const char string151[] = "truncate";
|
|
const char string152[] = "round";
|
|
const char string153[] = "char-code";
|
|
const char string154[] = "code-char";
|
|
const char string155[] = "characterp";
|
|
const char string156[] = "stringp";
|
|
const char string157[] = "string=";
|
|
const char string158[] = "string<";
|
|
const char string159[] = "string>";
|
|
const char string160[] = "string/=";
|
|
const char string161[] = "string<=";
|
|
const char string162[] = "string>=";
|
|
const char string163[] = "sort";
|
|
const char string164[] = "concatenate";
|
|
const char string165[] = "subseq";
|
|
const char string166[] = "search";
|
|
const char string167[] = "read-from-string";
|
|
const char string168[] = "princ-to-string";
|
|
const char string169[] = "prin1-to-string";
|
|
const char string170[] = "logand";
|
|
const char string171[] = "logior";
|
|
const char string172[] = "logxor";
|
|
const char string173[] = "lognot";
|
|
const char string174[] = "ash";
|
|
const char string175[] = "logbitp";
|
|
const char string176[] = "eval";
|
|
const char string177[] = "return";
|
|
const char string178[] = "globals";
|
|
const char string179[] = "locals";
|
|
const char string180[] = "makunbound";
|
|
const char string181[] = "break";
|
|
const char string182[] = "read";
|
|
const char string183[] = "prin1";
|
|
const char string184[] = "print";
|
|
const char string185[] = "princ";
|
|
const char string186[] = "terpri";
|
|
const char string187[] = "read-byte";
|
|
const char string188[] = "read-line";
|
|
const char string189[] = "write-byte";
|
|
const char string190[] = "write-string";
|
|
const char string191[] = "write-line";
|
|
const char string192[] = "restart-i2c";
|
|
const char string193[] = "gc";
|
|
const char string194[] = "room";
|
|
const char string195[] = "backtrace";
|
|
const char string196[] = "save-image";
|
|
const char string197[] = "load-image";
|
|
const char string198[] = "cls";
|
|
const char string199[] = "digitalread";
|
|
const char string200[] = "analogreadresolution";
|
|
const char string201[] = "analogwrite";
|
|
const char string202[] = "analogwriteresolution";
|
|
const char string203[] = "delay";
|
|
const char string204[] = "millis";
|
|
const char string205[] = "sleep";
|
|
const char string206[] = "note";
|
|
const char string207[] = "edit";
|
|
const char string208[] = "pprint";
|
|
const char string209[] = "pprintall";
|
|
const char string210[] = "require";
|
|
const char string211[] = "list-library";
|
|
const char string212[] = "?";
|
|
const char string213[] = "documentation";
|
|
const char string214[] = "apropos";
|
|
const char string215[] = "apropos-list";
|
|
const char string216[] = "unwind-protect";
|
|
const char string217[] = "ignore-errors";
|
|
const char string218[] = "error";
|
|
const char string219[] = "directory";
|
|
const char string220[] = "with-client";
|
|
const char string221[] = "available";
|
|
const char string222[] = "wifi-server";
|
|
const char string223[] = "wifi-softap";
|
|
const char string224[] = "connected";
|
|
const char string225[] = "wifi-localip";
|
|
const char string226[] = "wifi-connect";
|
|
const char string227[] = "with-gfx";
|
|
const char string228[] = "draw-pixel";
|
|
const char string229[] = "draw-line";
|
|
const char string230[] = "draw-rect";
|
|
const char string231[] = "fill-rect";
|
|
const char string232[] = "draw-circle";
|
|
const char string233[] = "fill-circle";
|
|
const char string234[] = "draw-round-rect";
|
|
const char string235[] = "fill-round-rect";
|
|
const char string236[] = "draw-triangle";
|
|
const char string237[] = "fill-triangle";
|
|
const char string238[] = "draw-char";
|
|
const char string239[] = "set-cursor";
|
|
const char string240[] = "set-text-color";
|
|
const char string241[] = "set-text-size";
|
|
const char string242[] = "set-text-wrap";
|
|
const char string243[] = "fill-screen";
|
|
const char string244[] = "set-rotation";
|
|
const char string245[] = "invert-display";
|
|
const char string246[] = ":led-builtin";
|
|
const char string247[] = ":high";
|
|
const char string248[] = ":low";
|
|
#if defined(CPU_ATSAMD21)
|
|
const char string249[] = ":input";
|
|
const char string250[] = ":input-pullup";
|
|
const char string251[] = ":input-pulldown";
|
|
const char string252[] = ":output";
|
|
const char string253[] = ":ar-default";
|
|
const char string254[] = ":ar-internal1v0";
|
|
const char string255[] = ":ar-internal1v65";
|
|
const char string256[] = ":ar-internal2v23";
|
|
const char string257[] = ":ar-external";
|
|
const char string258[] = ":pa-dir";
|
|
const char string259[] = ":pa-dirclr";
|
|
const char string260[] = ":pa-dirset";
|
|
const char string261[] = ":pa-dirtgl";
|
|
const char string262[] = ":pa-out";
|
|
const char string263[] = ":pa-outclr";
|
|
const char string264[] = ":pa-outset";
|
|
const char string265[] = ":pa-outtgl";
|
|
const char string266[] = ":pa-in";
|
|
const char string267[] = ":pb-dir";
|
|
const char string268[] = ":pb-dirclr";
|
|
const char string269[] = ":pb-dirset";
|
|
const char string270[] = ":pb-dirtgl";
|
|
const char string271[] = ":pb-out";
|
|
const char string272[] = ":pb-outclr";
|
|
const char string273[] = ":pb-outset";
|
|
const char string274[] = ":pb-outtgl";
|
|
const char string275[] = ":pb-in";
|
|
#elif defined(CPU_ATSAMD51)
|
|
const char string249[] = ":input";
|
|
const char string250[] = ":input-pullup";
|
|
const char string251[] = ":input-pulldown";
|
|
const char string252[] = ":output";
|
|
const char string253[] = ":ar-default";
|
|
const char string254[] = ":ar-internal1v0";
|
|
const char string255[] = ":ar-internal1v1";
|
|
const char string256[] = ":ar-internal1v2";
|
|
const char string257[] = ":ar-internal1v25";
|
|
const char string258[] = ":ar-internal1v65";
|
|
const char string259[] = ":ar-internal2v0";
|
|
const char string260[] = ":ar-internal2v2";
|
|
const char string261[] = ":ar-internal2v23";
|
|
const char string262[] = ":ar-internal2v4";
|
|
const char string263[] = ":ar-internal2v5";
|
|
const char string264[] = ":ar-external";
|
|
const char string265[] = ":pa-dir";
|
|
const char string266[] = ":pa-dirclr";
|
|
const char string267[] = ":pa-dirset";
|
|
const char string268[] = ":pa-dirtgl";
|
|
const char string269[] = ":pa-out";
|
|
const char string270[] = ":pa-outclr";
|
|
const char string271[] = ":pa-outset";
|
|
const char string272[] = ":pa-outtgl";
|
|
const char string273[] = ":pa-in";
|
|
const char string274[] = ":pb-dir";
|
|
const char string275[] = ":pb-dirclr";
|
|
const char string276[] = ":pb-dirset";
|
|
const char string277[] = ":pb-dirtgl";
|
|
const char string278[] = ":pb-out";
|
|
const char string279[] = ":pb-outclr";
|
|
const char string280[] = ":pb-outset";
|
|
const char string281[] = ":pb-outtgl";
|
|
const char string282[] = ":pb-in";
|
|
#elif defined(CPU_NRF51822)
|
|
const char string249[] = ":input";
|
|
const char string250[] = ":input-pullup";
|
|
const char string251[] = ":input-pulldown";
|
|
const char string252[] = ":output";
|
|
const char string253[] = ":ar-default";
|
|
const char string254[] = ":ar-vbg";
|
|
const char string255[] = ":ar-supply-one-half";
|
|
const char string256[] = ":ar-supply-one-third";
|
|
const char string257[] = ":ar-ext0";
|
|
const char string258[] = ":ar-ext1";
|
|
const char string259[] = ":p0-out";
|
|
const char string260[] = ":p0-outset";
|
|
const char string261[] = ":p0-outclr";
|
|
const char string262[] = ":p0-in";
|
|
const char string263[] = ":p0-dir";
|
|
const char string264[] = ":p0-dirset";
|
|
const char string265[] = ":p0-dirclr";
|
|
#elif defined(CPU_NRF52840)
|
|
const char string249[] = ":input";
|
|
const char string250[] = ":input-pullup";
|
|
const char string251[] = ":input-pulldown";
|
|
const char string252[] = ":output";
|
|
const char string253[] = ":ar-default";
|
|
const char string254[] = ":ar-internal";
|
|
const char string255[] = ":ar-internal-3-0";
|
|
const char string256[] = ":ar-internal-2-4";
|
|
const char string257[] = ":ar-internal-1-8";
|
|
const char string258[] = ":ar-internal-1-2";
|
|
const char string259[] = ":ar-vdd4";
|
|
const char string260[] = ":p0-out";
|
|
const char string261[] = ":p0-outset";
|
|
const char string262[] = ":p0-outclr";
|
|
const char string263[] = ":p0-in";
|
|
const char string264[] = ":p0-dir";
|
|
const char string265[] = ":p0-dirset";
|
|
const char string266[] = ":p0-dirclr";
|
|
const char string267[] = ":p1-out";
|
|
const char string268[] = ":p1-outset";
|
|
const char string269[] = ":p1-outclr";
|
|
const char string270[] = ":p1-in";
|
|
const char string271[] = ":p1-dir";
|
|
const char string272[] = ":p1-dirset";
|
|
const char string273[] = ":p1-dirclr";
|
|
#elif defined(CPU_NRF52833)
|
|
const char string249[] = ":input";
|
|
const char string250[] = ":input-pullup";
|
|
const char string251[] = ":input-pulldown";
|
|
const char string252[] = ":output";
|
|
const char string253[] = ":ar-default";
|
|
const char string254[] = ":ar-internal";
|
|
const char string255[] = ":ar-vdd4";
|
|
const char string256[] = ":p0-out";
|
|
const char string257[] = ":p0-outset";
|
|
const char string258[] = ":p0-outclr";
|
|
const char string259[] = ":p0-in";
|
|
const char string260[] = ":p0-dir";
|
|
const char string261[] = ":p0-dirset";
|
|
const char string262[] = ":p0-dirclr";
|
|
const char string263[] = ":p1-out";
|
|
const char string264[] = ":p1-outset";
|
|
const char string265[] = ":p1-outclr";
|
|
const char string266[] = ":p1-in";
|
|
const char string267[] = ":p1-dir";
|
|
const char string268[] = ":p1-dirset";
|
|
const char string269[] = ":p1-dirclr";
|
|
#elif defined(CPU_iMXRT1062)
|
|
const char string249[] = ":input";
|
|
const char string250[] = ":input-pullup";
|
|
const char string251[] = ":input-pulldown";
|
|
const char string252[] = ":output";
|
|
const char string253[] = ":output-opendrain";
|
|
#elif defined(CPU_MAX32620)
|
|
const char string249[] = ":input";
|
|
const char string250[] = ":input-pullup";
|
|
const char string251[] = ":output";
|
|
const char string252[] = ":default";
|
|
const char string253[] = ":external";
|
|
#elif defined(CPU_RP2040)
|
|
const char string249[] = ":input";
|
|
const char string250[] = ":input-pullup";
|
|
const char string251[] = ":input-pulldown";
|
|
const char string252[] = ":output";
|
|
const char string253[] = ":gpio-in";
|
|
const char string254[] = ":gpio-out";
|
|
const char string255[] = ":gpio-out-set";
|
|
const char string256[] = ":gpio-out-clr";
|
|
const char string257[] = ":gpio-out-xor";
|
|
const char string258[] = ":gpio-oe";
|
|
const char string259[] = ":gpio-oe-set";
|
|
const char string260[] = ":gpio-oe-clr";
|
|
const char string261[] = ":gpio-oe-xor";
|
|
#elif defined(CPU_RP2350)
|
|
const char string249[] = ":input";
|
|
const char string250[] = ":input-pullup";
|
|
const char string251[] = ":input-pulldown";
|
|
const char string252[] = ":output";
|
|
const char string253[] = ":gpio-in";
|
|
const char string254[] = ":gpio-out";
|
|
const char string255[] = ":gpio-out-set";
|
|
const char string256[] = ":gpio-out-clr";
|
|
const char string257[] = ":gpio-out-xor";
|
|
const char string258[] = ":gpio-oe";
|
|
const char string259[] = ":gpio-oe-set";
|
|
const char string260[] = ":gpio-oe-clr";
|
|
const char string261[] = ":gpio-oe-xor";
|
|
#elif defined(CPU_RA4M1)
|
|
const char string249[] = ":input";
|
|
const char string250[] = ":input-pullup";
|
|
const char string251[] = ":output";
|
|
const char string252[] = ":output-opendrain";
|
|
const char string253[] = ":ar-default";
|
|
const char string254[] = ":ar-internal";
|
|
const char string255[] = ":ar-external";
|
|
#endif
|
|
|
|
// Documentation strings
|
|
const char doc0[] = "nil\n"
|
|
"A symbol equivalent to the empty list (). Also represents false.";
|
|
const char doc1[] = "t\n"
|
|
"A symbol representing true.";
|
|
const char doc2[] = "nothing\n"
|
|
"A symbol with no value.\n"
|
|
"It is useful if you want to suppress printing the result of evaluating a function.";
|
|
const char doc3[] = "&optional\n"
|
|
"Can be followed by one or more optional parameters in a lambda or defun parameter list.";
|
|
const char doc4[] = "*features*\n"
|
|
"Returns a list of keywords representing features supported by this platform.";
|
|
const char doc12[] = "&rest\n"
|
|
"Can be followed by a parameter in a lambda or defun parameter list,\n"
|
|
"and is assigned a list of the corresponding arguments.";
|
|
const char doc13[] = "(lambda (parameter*) form*)\n"
|
|
"Creates an unnamed function with parameters. The body is evaluated with the parameters as local variables\n"
|
|
"whose initial values are defined by the values of the forms after the lambda form.";
|
|
const char doc14[] = "(let ((var value) ... ) forms*)\n"
|
|
"Declares local variables with values, and evaluates the forms with those local variables.";
|
|
const char doc15[] = "(let* ((var value) ... ) forms*)\n"
|
|
"Declares local variables with values, and evaluates the forms with those local variables.\n"
|
|
"Each declaration can refer to local variables that have been defined earlier in the let*.";
|
|
const char doc19[] = "(defun name (parameters) form*)\n"
|
|
"Defines a function.";
|
|
const char doc20[] = "(defvar variable form)\n"
|
|
"Defines a global variable.";
|
|
const char doc21[] = "(defcode name (parameters) form*)\n"
|
|
"Creates a machine-code function called name from a series of 16-bit integers given in the body of the form.\n"
|
|
"These are written into RAM, and can be executed by calling the function in the same way as a normal Lisp function.";
|
|
const char doc22[] = "(eq item item)\n"
|
|
"Tests whether the two arguments are the same symbol, same character, equal numbers,\n"
|
|
"or point to the same cons, and returns t or nil as appropriate.";
|
|
const char doc23[] = "(car list)\n"
|
|
"Returns the first item in a list.";
|
|
const char doc24[] = "(first list)\n"
|
|
"Returns the first item in a list. Equivalent to car.";
|
|
const char doc25[] = "(cdr list)\n"
|
|
"Returns a list with the first item removed.";
|
|
const char doc26[] = "(rest list)\n"
|
|
"Returns a list with the first item removed. Equivalent to cdr.";
|
|
const char doc27[] = "(nth number list)\n"
|
|
"Returns the nth item in list, counting from zero.";
|
|
const char doc28[] = "(aref array index [index*])\n"
|
|
"Returns an element from the specified array.";
|
|
const char doc29[] = "(char string n)\n"
|
|
"Returns the nth character in a string, counting from zero.";
|
|
const char doc30[] = "(string item)\n"
|
|
"Converts its argument to a string.";
|
|
const char doc31[] = "(pinmode pin mode)\n"
|
|
"Sets the input/output mode of an Arduino pin number, and returns nil.\n"
|
|
"The mode parameter can be an integer, a keyword, or t or nil.";
|
|
const char doc32[] = "(digitalwrite pin state)\n"
|
|
"Sets the state of the specified Arduino pin number.";
|
|
const char doc33[] = "(analogread pin)\n"
|
|
"Reads the specified Arduino analogue pin number and returns the value.";
|
|
const char doc34[] = "(analogreference keyword)\n"
|
|
"Specifies a keyword to set the analogue reference voltage used for analogue input.";
|
|
const char doc35[] = "(register address [value])\n"
|
|
"Reads or writes the value of a peripheral register.\n"
|
|
"If value is not specified the function returns the value of the register at address.\n"
|
|
"If value is specified the value is written to the register at address and the function returns value.";
|
|
const char doc36[] = "(format output controlstring [arguments]*)\n"
|
|
"Outputs its arguments formatted according to the format directives in controlstring.";
|
|
const char doc37[] = "(or item*)\n"
|
|
"Evaluates its arguments until one returns non-nil, and returns its value.";
|
|
const char doc38[] = "(setq symbol value [symbol value]*)\n"
|
|
"For each pair of arguments assigns the value of the second argument\n"
|
|
"to the variable specified in the first argument.";
|
|
const char doc39[] = "(loop forms*)\n"
|
|
"Executes its arguments repeatedly until one of the arguments calls (return),\n"
|
|
"which then causes an exit from the loop.";
|
|
const char doc40[] = "(push item place)\n"
|
|
"Modifies the value of place, which should be a list, to add item onto the front of the list,\n"
|
|
"and returns the new list.";
|
|
const char doc41[] = "(pop place)\n"
|
|
"Modifies the value of place, which should be a non-nil list, to remove its first item,\n"
|
|
"and returns that item.";
|
|
const char doc42[] = "(incf place [number])\n"
|
|
"Increments a place, which should have an numeric value, and returns the result.\n"
|
|
"The third argument is an optional increment which defaults to 1.";
|
|
const char doc43[] = "(decf place [number])\n"
|
|
"Decrements a place, which should have an numeric value, and returns the result.\n"
|
|
"The third argument is an optional decrement which defaults to 1.";
|
|
const char doc44[] = "(setf place value [place value]*)\n"
|
|
"For each pair of arguments modifies a place to the result of evaluating value.";
|
|
const char doc45[] = "(dolist (var list [result]) form*)\n"
|
|
"Sets the local variable var to each element of list in turn, and executes the forms.\n"
|
|
"It then returns result, or nil if result is omitted.";
|
|
const char doc46[] = "(dotimes (var number [result]) form*)\n"
|
|
"Executes the forms number times, with the local variable var set to each integer from 0 to number-1 in turn.\n"
|
|
"It then returns result, or nil if result is omitted.";
|
|
const char doc47[] = "(do ((var [init [step]])*) (end-test result*) form*)\n"
|
|
"Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step sequentially.\n"
|
|
"The forms are executed until end-test is true. It returns result.";
|
|
const char doc48[] = "(do* ((var [init [step]])*) (end-test result*) form*)\n"
|
|
"Accepts an arbitrary number of iteration vars, which are initialised to init and stepped by step in parallel.\n"
|
|
"The forms are executed until end-test is true. It returns result.";
|
|
const char doc49[] = "(trace [function]*)\n"
|
|
"Turns on tracing of up to TRACEMAX user-defined functions,\n"
|
|
"and returns a list of the functions currently being traced.";
|
|
const char doc50[] = "(untrace [function]*)\n"
|
|
"Turns off tracing of up to TRACEMAX user-defined functions, and returns a list of the functions untraced.\n"
|
|
"If no functions are specified it untraces all functions.";
|
|
const char doc51[] = "(for-millis ([number]) form*)\n"
|
|
"Executes the forms and then waits until a total of number milliseconds have elapsed.\n"
|
|
"Returns the total number of milliseconds taken.";
|
|
const char doc52[] = "(time form)\n"
|
|
"Prints the value returned by the form, and the time taken to evaluate the form\n"
|
|
"in milliseconds or seconds.";
|
|
const char doc53[] = "(with-output-to-string (str) form*)\n"
|
|
"Returns a string containing the output to the stream variable str.";
|
|
const char doc54[] = "(with-serial (str port [baud]) form*)\n"
|
|
"Evaluates the forms with str bound to a serial-stream using port.\n"
|
|
"The optional baud gives the baud rate divided by 100, default 96.";
|
|
const char doc55[] = "(with-i2c (str [port] address [read-p]) form*)\n"
|
|
"Evaluates the forms with str bound to an i2c-stream defined by address.\n"
|
|
"If read-p is nil or omitted the stream is written to, otherwise it specifies the number of bytes\n"
|
|
"to be read from the stream. If port is omitted it defaults to 0, otherwise it specifies the port, 0 or 1.";
|
|
const char doc56[] = "(with-spi (str pin [clock] [bitorder] [mode] [port]) form*)\n"
|
|
"Evaluates the forms with str bound to an spi-stream.\n"
|
|
"The parameters specify the enable pin, clock in kHz (default 4000),\n"
|
|
"bitorder 0 for LSBFIRST and 1 for MSBFIRST (default 1), SPI mode (default 0), and port 0 or 1 (default 0).";
|
|
const char doc57[] = "(with-sd-card (str filename [mode]) form*)\n"
|
|
"Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename.\n"
|
|
"If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite.";
|
|
const char doc58[] = "(progn form*)\n"
|
|
"Evaluates several forms grouped together into a block, and returns the result of evaluating the last form.";
|
|
const char doc59[] = "(if test then [else])\n"
|
|
"Evaluates test. If it's non-nil the form then is evaluated and returned;\n"
|
|
"otherwise the form else is evaluated and returned.";
|
|
const char doc60[] = "(cond ((test form*) (test form*) ... ))\n"
|
|
"Each argument is a list consisting of a test optionally followed by one or more forms.\n"
|
|
"If the test evaluates to non-nil the forms are evaluated, and the last value is returned as the result of the cond.\n"
|
|
"If the test evaluates to nil, none of the forms are evaluated, and the next argument is processed in the same way.";
|
|
const char doc61[] = "(when test form*)\n"
|
|
"Evaluates the test. If it's non-nil the forms are evaluated and the last value is returned.";
|
|
const char doc62[] = "(unless test form*)\n"
|
|
"Evaluates the test. If it's nil the forms are evaluated and the last value is returned.";
|
|
const char doc63[] = "(case keyform ((key form*) (key form*) ... ))\n"
|
|
"Evaluates a keyform to produce a test key, and then tests this against a series of arguments,\n"
|
|
"each of which is a list containing a key optionally followed by one or more forms.";
|
|
const char doc64[] = "(and item*)\n"
|
|
"Evaluates its arguments until one returns nil, and returns the last value.";
|
|
const char doc65[] = "(not item)\n"
|
|
"Returns t if its argument is nil, or nil otherwise. Equivalent to null.";
|
|
const char doc66[] = "(null list)\n"
|
|
"Returns t if its argument is nil, or nil otherwise. Equivalent to not.";
|
|
const char doc67[] = "(cons item item)\n"
|
|
"If the second argument is a list, cons returns a new list with item added to the front of the list.\n"
|
|
"If the second argument isn't a list cons returns a dotted pair.";
|
|
const char doc68[] = "(atom item)\n"
|
|
"Returns t if its argument is a single number, symbol, or nil.";
|
|
const char doc69[] = "(listp item)\n"
|
|
"Returns t if its argument is a list.";
|
|
const char doc70[] = "(consp item)\n"
|
|
"Returns t if its argument is a non-null list.";
|
|
const char doc71[] = "(symbolp item)\n"
|
|
"Returns t if its argument is a symbol.";
|
|
const char doc72[] = "(arrayp item)\n"
|
|
"Returns t if its argument is an array.";
|
|
const char doc73[] = "(boundp item)\n"
|
|
"Returns t if its argument is a symbol with a value.";
|
|
const char doc74[] = "(keywordp item)\n"
|
|
"Returns t if its argument is a built-in or user-defined keyword.";
|
|
const char doc75[] = "(set symbol value [symbol value]*)\n"
|
|
"For each pair of arguments, assigns the value of the second argument to the value of the first argument.";
|
|
const char doc76[] = "(streamp item)\n"
|
|
"Returns t if its argument is a stream.";
|
|
const char doc77[] = "(equal item item)\n"
|
|
"Tests whether the two arguments are the same symbol, same character, equal numbers,\n"
|
|
"or point to the same cons, and returns t or nil as appropriate.";
|
|
const char doc78[] = "(caar list)";
|
|
const char doc79[] = "(cadr list)";
|
|
const char doc80[] = "(second list)\n"
|
|
"Returns the second item in a list. Equivalent to cadr.";
|
|
const char doc81[] = "(cdar list)\n"
|
|
"Equivalent to (cdr (car list)).";
|
|
const char doc82[] = "(cddr list)\n"
|
|
"Equivalent to (cdr (cdr list)).";
|
|
const char doc83[] = "(caaar list)\n"
|
|
"Equivalent to (car (car (car list))).";
|
|
const char doc84[] = "(caadr list)\n"
|
|
"Equivalent to (car (car (cdar list))).";
|
|
const char doc85[] = "(cadar list)\n"
|
|
"Equivalent to (car (cdr (car list))).";
|
|
const char doc86[] = "(caddr list)\n"
|
|
"Equivalent to (car (cdr (cdr list))).";
|
|
const char doc87[] = "(third list)\n"
|
|
"Returns the third item in a list. Equivalent to caddr.";
|
|
const char doc88[] = "(cdaar list)\n"
|
|
"Equivalent to (cdar (car (car list))).";
|
|
const char doc89[] = "(cdadr list)\n"
|
|
"Equivalent to (cdr (car (cdr list))).";
|
|
const char doc90[] = "(cddar list)\n"
|
|
"Equivalent to (cdr (cdr (car list))).";
|
|
const char doc91[] = "(cdddr list)\n"
|
|
"Equivalent to (cdr (cdr (cdr list))).";
|
|
const char doc92[] = "(length item)\n"
|
|
"Returns the number of items in a list, the length of a string, or the length of a one-dimensional array.";
|
|
const char doc93[] = "(array-dimensions item)\n"
|
|
"Returns a list of the dimensions of an array.";
|
|
const char doc94[] = "(list item*)\n"
|
|
"Returns a list of the values of its arguments.";
|
|
const char doc95[] = "(copy-list list)\n"
|
|
"Returns a copy of a list.";
|
|
const char doc96[] = "(make-array size [:initial-element element] [:element-type 'bit])\n"
|
|
"If size is an integer it creates a one-dimensional array with elements from 0 to size-1.\n"
|
|
"If size is a list of n integers it creates an n-dimensional array with those dimensions.\n"
|
|
"If :element-type 'bit is specified the array is a bit array.";
|
|
const char doc97[] = "(reverse list)\n"
|
|
"Returns a list with the elements of list in reverse order.";
|
|
const char doc98[] = "(assoc key list [:test function])\n"
|
|
"Looks up a key in an association list of (key . value) pairs, using eq or the specified test function,\n"
|
|
"and returns the matching pair, or nil if no pair is found.";
|
|
const char doc99[] = "(member item list [:test function])\n"
|
|
"Searches for an item in a list, using eq or the specified test function, and returns the list starting\n"
|
|
"from the first occurrence of the item, or nil if it is not found.";
|
|
const char doc100[] = "(apply function list)\n"
|
|
"Returns the result of evaluating function, with the list of arguments specified by the second parameter.";
|
|
const char doc101[] = "(funcall function argument*)\n"
|
|
"Evaluates function with the specified arguments.";
|
|
const char doc102[] = "(append list*)\n"
|
|
"Joins its arguments, which should be lists, into a single list.";
|
|
const char doc103[] = "(mapc function list1 [list]*)\n"
|
|
"Applies the function to each element in one or more lists, ignoring the results.\n"
|
|
"It returns the first list argument.";
|
|
const char doc104[] = "(mapl function list1 [list]*)\n"
|
|
"Applies the function to one or more lists and then successive cdrs of those lists,\n"
|
|
"ignoring the results. It returns the first list argument.";
|
|
const char doc105[] = "(mapcar function list1 [list]*)\n"
|
|
"Applies the function to each element in one or more lists, and returns the resulting list.";
|
|
const char doc106[] = "(mapcan function list1 [list]*)\n"
|
|
"Applies the function to each element in one or more lists. The results should be lists,\n"
|
|
"and these are destructively concatenated together to give the value returned.";
|
|
const char doc107[] = "(maplist function list1 [list]*)\n"
|
|
"Applies the function to one or more lists and then successive cdrs of those lists,\n"
|
|
"and returns the resulting list.";
|
|
const char doc108[] = "(mapcon function list1 [list]*)\n"
|
|
"Applies the function to one or more lists and then successive cdrs of those lists,\n"
|
|
"and these are destructively concatenated together to give the value returned.";
|
|
const char doc109[] = "(+ number*)\n"
|
|
"Adds its arguments together.\n"
|
|
"If each argument is an integer, and the running total doesn't overflow, the result is an integer,\n"
|
|
"otherwise a floating-point number.";
|
|
const char doc110[] = "(- number*)\n"
|
|
"If there is one argument, negates the argument.\n"
|
|
"If there are two or more arguments, subtracts the second and subsequent arguments from the first argument.\n"
|
|
"If each argument is an integer, and the running total doesn't overflow, returns the result as an integer,\n"
|
|
"otherwise a floating-point number.";
|
|
const char doc111[] = "(* number*)\n"
|
|
"Multiplies its arguments together.\n"
|
|
"If each argument is an integer, and the running total doesn't overflow, the result is an integer,\n"
|
|
"otherwise it's a floating-point number.";
|
|
const char doc112[] = "(/ number*)\n"
|
|
"Divides the first argument by the second and subsequent arguments.\n"
|
|
"If each argument is an integer, and each division produces an exact result, the result is an integer;\n"
|
|
"otherwise it's a floating-point number.";
|
|
const char doc113[] = "(mod number number)\n"
|
|
"Returns its first argument modulo the second argument.\n"
|
|
"If both arguments are integers the result is an integer; otherwise it's a floating-point number.";
|
|
const char doc114[] = "(rem number number)\n"
|
|
"Returns the remainder from dividing the first argument by the second argument.\n"
|
|
"If both arguments are integers the result is an integer; otherwise it's a floating-point number.";
|
|
const char doc115[] = "(1+ number)\n"
|
|
"Adds one to its argument and returns it.\n"
|
|
"If the argument is an integer the result is an integer if possible;\n"
|
|
"otherwise it's a floating-point number.";
|
|
const char doc116[] = "(1- number)\n"
|
|
"Subtracts one from its argument and returns it.\n"
|
|
"If the argument is an integer the result is an integer if possible;\n"
|
|
"otherwise it's a floating-point number.";
|
|
const char doc117[] = "(abs number)\n"
|
|
"Returns the absolute, positive value of its argument.\n"
|
|
"If the argument is an integer the result will be returned as an integer if possible,\n"
|
|
"otherwise a floating-point number.";
|
|
const char doc118[] = "(random number)\n"
|
|
"If number is an integer returns a random number between 0 and one less than its argument.\n"
|
|
"Otherwise returns a floating-point number between zero and number.";
|
|
const char doc119[] = "(max number*)\n"
|
|
"Returns the maximum of one or more arguments.";
|
|
const char doc120[] = "(min number*)\n"
|
|
"Returns the minimum of one or more arguments.";
|
|
const char doc121[] = "(/= number*)\n"
|
|
"Returns t if none of the arguments are equal, or nil if two or more arguments are equal.";
|
|
const char doc122[] = "(= number*)\n"
|
|
"Returns t if all the arguments, which must be numbers, are numerically equal, and nil otherwise.";
|
|
const char doc123[] = "(< number*)\n"
|
|
"Returns t if each argument is less than the next argument, and nil otherwise.";
|
|
const char doc124[] = "(<= number*)\n"
|
|
"Returns t if each argument is less than or equal to the next argument, and nil otherwise.";
|
|
const char doc125[] = "(> number*)\n"
|
|
"Returns t if each argument is greater than the next argument, and nil otherwise.";
|
|
const char doc126[] = "(>= number*)\n"
|
|
"Returns t if each argument is greater than or equal to the next argument, and nil otherwise.";
|
|
const char doc127[] = "(plusp number)\n"
|
|
"Returns t if the argument is greater than zero, or nil otherwise.";
|
|
const char doc128[] = "(minusp number)\n"
|
|
"Returns t if the argument is less than zero, or nil otherwise.";
|
|
const char doc129[] = "(zerop number)\n"
|
|
"Returns t if the argument is zero.";
|
|
const char doc130[] = "(oddp number)\n"
|
|
"Returns t if the integer argument is odd.";
|
|
const char doc131[] = "(evenp number)\n"
|
|
"Returns t if the integer argument is even.";
|
|
const char doc132[] = "(integerp number)\n"
|
|
"Returns t if the argument is an integer.";
|
|
const char doc133[] = "(numberp number)\n"
|
|
"Returns t if the argument is a number.";
|
|
const char doc134[] = "(float number)\n"
|
|
"Returns its argument converted to a floating-point number.";
|
|
const char doc135[] = "(floatp number)\n"
|
|
"Returns t if the argument is a floating-point number.";
|
|
const char doc136[] = "(sin number)\n"
|
|
"Returns sin(number).";
|
|
const char doc137[] = "(cos number)\n"
|
|
"Returns cos(number).";
|
|
const char doc138[] = "(tan number)\n"
|
|
"Returns tan(number).";
|
|
const char doc139[] = "(asin number)\n"
|
|
"Returns asin(number).";
|
|
const char doc140[] = "(acos number)\n"
|
|
"Returns acos(number).";
|
|
const char doc141[] = "(atan number1 [number2])\n"
|
|
"Returns the arc tangent of number1/number2, in radians. If number2 is omitted it defaults to 1.";
|
|
const char doc142[] = "(sinh number)\n"
|
|
"Returns sinh(number).";
|
|
const char doc143[] = "(cosh number)\n"
|
|
"Returns cosh(number).";
|
|
const char doc144[] = "(tanh number)\n"
|
|
"Returns tanh(number).";
|
|
const char doc145[] = "(exp number)\n"
|
|
"Returns exp(number).";
|
|
const char doc146[] = "(sqrt number)\n"
|
|
"Returns sqrt(number).";
|
|
const char doc147[] = "(log number [base])\n"
|
|
"Returns the logarithm of number to the specified base. If base is omitted it defaults to e.";
|
|
const char doc148[] = "(expt number power)\n"
|
|
"Returns number raised to the specified power.\n"
|
|
"Returns the result as an integer if the arguments are integers and the result will be within range,\n"
|
|
"otherwise a floating-point number.";
|
|
const char doc149[] = "(ceiling number [divisor])\n"
|
|
"Returns ceil(number/divisor). If omitted, divisor is 1.";
|
|
const char doc150[] = "(floor number [divisor])\n"
|
|
"Returns floor(number/divisor). If omitted, divisor is 1.";
|
|
const char doc151[] = "(truncate number [divisor])\n"
|
|
"Returns the integer part of number/divisor. If divisor is omitted it defaults to 1.";
|
|
const char doc152[] = "(round number [divisor])\n"
|
|
"Returns the integer closest to number/divisor. If divisor is omitted it defaults to 1.";
|
|
const char doc153[] = "(char-code character)\n"
|
|
"Returns the ASCII code for a character, as an integer.";
|
|
const char doc154[] = "(code-char integer)\n"
|
|
"Returns the character for the specified ASCII code.";
|
|
const char doc155[] = "(characterp item)\n"
|
|
"Returns t if the argument is a character and nil otherwise.";
|
|
const char doc156[] = "(stringp item)\n"
|
|
"Returns t if the argument is a string and nil otherwise.";
|
|
const char doc157[] = "(string= string string)\n"
|
|
"Returns t if the two strings are the same, or nil otherwise.";
|
|
const char doc158[] = "(string< string string)\n"
|
|
"Returns the index to the first mismatch if the first string is alphabetically less than the second string,\n"
|
|
"or nil otherwise.";
|
|
const char doc159[] = "(string> string string)\n"
|
|
"Returns the index to the first mismatch if the first string is alphabetically greater than the second string,\n"
|
|
"or nil otherwise.";
|
|
const char doc160[] = "(string/= string string)\n"
|
|
"Returns the index to the first mismatch if the two strings are not the same, or nil otherwise.";
|
|
const char doc161[] = "(string<= string string)\n"
|
|
"Returns the index to the first mismatch if the first string is alphabetically less than or equal to\n"
|
|
"the second string, or nil otherwise.";
|
|
const char doc162[] = "(string>= string string)\n"
|
|
"Returns the index to the first mismatch if the first string is alphabetically greater than or equal to\n"
|
|
"the second string, or nil otherwise.";
|
|
const char doc163[] = "(sort list test)\n"
|
|
"Destructively sorts list according to the test function, using an insertion sort, and returns the sorted list.";
|
|
const char doc164[] = "(concatenate 'string string*)\n"
|
|
"Joins together the strings given in the second and subsequent arguments, and returns a single string.";
|
|
const char doc165[] = "(subseq seq start [end])\n"
|
|
"Returns a subsequence of a list or string from item start to item end-1.";
|
|
const char doc166[] = "(search pattern target [:test function])\n"
|
|
"Returns the index of the first occurrence of pattern in target, or nil if it's not found.\n"
|
|
"The target can be a list or string. If it's a list a test function can be specified; default eq.";
|
|
const char doc167[] = "(read-from-string string)\n"
|
|
"Reads an atom or list from the specified string and returns it.";
|
|
const char doc168[] = "(princ-to-string item)\n"
|
|
"Prints its argument to a string, and returns the string.\n"
|
|
"Characters and strings are printed without quotation marks or escape characters.";
|
|
const char doc169[] = "(prin1-to-string item [stream])\n"
|
|
"Prints its argument to a string, and returns the string.\n"
|
|
"Characters and strings are printed with quotation marks and escape characters,\n"
|
|
"in a format that will be suitable for read-from-string.";
|
|
const char doc170[] = "(logand [value*])\n"
|
|
"Returns the bitwise & of the values.";
|
|
const char doc171[] = "(logior [value*])\n"
|
|
"Returns the bitwise | of the values.";
|
|
const char doc172[] = "(logxor [value*])\n"
|
|
"Returns the bitwise ^ of the values.";
|
|
const char doc173[] = "(lognot value)\n"
|
|
"Returns the bitwise logical NOT of the value.";
|
|
const char doc174[] = "(ash value shift)\n"
|
|
"Returns the result of bitwise shifting value by shift bits. If shift is positive, value is shifted to the left.";
|
|
const char doc175[] = "(logbitp bit value)\n"
|
|
"Returns t if bit number bit in value is a '1', and nil if it is a '0'.";
|
|
const char doc176[] = "(eval form*)\n"
|
|
"Evaluates its argument an extra time.";
|
|
const char doc177[] = "(return [value])\n"
|
|
"Exits from a (dotimes ...), (dolist ...), or (loop ...) loop construct and returns value.";
|
|
const char doc178[] = "(globals)\n"
|
|
"Returns a list of global variables.";
|
|
const char doc179[] = "(locals)\n"
|
|
"Returns an association list of local variables and their values.";
|
|
const char doc180[] = "(makunbound symbol)\n"
|
|
"Removes the value of the symbol from GlobalEnv and returns the symbol.";
|
|
const char doc181[] = "(break)\n"
|
|
"Inserts a breakpoint in the program. When evaluated prints Break! and reenters the REPL.";
|
|
const char doc182[] = "(read [stream])\n"
|
|
"Reads an atom or list from the serial input and returns it.\n"
|
|
"If stream is specified the item is read from the specified stream.";
|
|
const char doc183[] = "(prin1 item [stream])\n"
|
|
"Prints its argument, and returns its value.\n"
|
|
"Strings are printed with quotation marks and escape characters.";
|
|
const char doc184[] = "(print item [stream])\n"
|
|
"Prints its argument with quotation marks and escape characters, on a new line, and followed by a space.\n"
|
|
"If stream is specified the argument is printed to the specified stream.";
|
|
const char doc185[] = "(princ item [stream])\n"
|
|
"Prints its argument, and returns its value.\n"
|
|
"Characters and strings are printed without quotation marks or escape characters.";
|
|
const char doc186[] = "(terpri [stream])\n"
|
|
"Prints a new line, and returns nil.\n"
|
|
"If stream is specified the new line is written to the specified stream.";
|
|
const char doc187[] = "(read-byte stream)\n"
|
|
"Reads a byte from a stream and returns it.";
|
|
const char doc188[] = "(read-line [stream])\n"
|
|
"Reads characters from the serial input up to a newline character, and returns them as a string, excluding the newline.\n"
|
|
"If stream is specified the line is read from the specified stream.";
|
|
const char doc189[] = "(write-byte number [stream])\n"
|
|
"Writes a byte to a stream.";
|
|
const char doc190[] = "(write-string string [stream])\n"
|
|
"Writes a string. If stream is specified the string is written to the stream.";
|
|
const char doc191[] = "(write-line string [stream])\n"
|
|
"Writes a string terminated by a newline character. If stream is specified the string is written to the stream.";
|
|
const char doc192[] = "(restart-i2c stream [read-p])\n"
|
|
"Restarts an i2c-stream.\n"
|
|
"If read-p is nil or omitted the stream is written to.\n"
|
|
"If read-p is an integer it specifies the number of bytes to be read from the stream.";
|
|
const char doc193[] = "(gc [print time])\n"
|
|
"Forces a garbage collection and prints the number of objects collected, and the time taken.";
|
|
const char doc194[] = "(room)\n"
|
|
"Returns the number of free Lisp cells remaining.";
|
|
const char doc195[] = "(backtrace [on])\n"
|
|
"Sets the state of backtrace according to the boolean flag 'on',\n"
|
|
"or with no argument displays the current state of backtrace.";
|
|
const char doc196[] = "(save-image [symbol])\n"
|
|
"Saves the current uLisp image to non-volatile memory or SD card so it can be loaded using load-image.";
|
|
const char doc197[] = "(load-image [filename])\n"
|
|
"Loads a saved uLisp image from non-volatile memory or SD card.";
|
|
const char doc198[] = "(cls)\n"
|
|
"Prints a clear-screen character.";
|
|
const char doc199[] = "(digitalread pin)\n"
|
|
"Reads the state of the specified Arduino pin number and returns t (high) or nil (low).";
|
|
const char doc200[] = "(analogreadresolution bits)\n"
|
|
"Specifies the resolution for the analogue inputs on platforms that support it.\n"
|
|
"The default resolution on all platforms is 10 bits.";
|
|
const char doc201[] = "(analogwrite pin value)\n"
|
|
"Writes the value to the specified Arduino pin number.";
|
|
const char doc202[] = "(analogwrite pin value)\n"
|
|
"Sets the analogue write resolution.";
|
|
const char doc203[] = "(delay number)\n"
|
|
"Delays for a specified number of milliseconds.";
|
|
const char doc204[] = "(millis)\n"
|
|
"Returns the time in milliseconds that uLisp has been running.";
|
|
const char doc205[] = "(sleep secs)\n"
|
|
"Puts the processor into a low-power sleep mode for secs.\n"
|
|
"Only supported on some platforms. On other platforms it does delay(1000*secs).";
|
|
const char doc206[] = "(note [pin] [note] [octave])\n"
|
|
"Generates a square wave on pin.\n"
|
|
"note represents the note in the well-tempered scale.\n"
|
|
"The argument octave can specify an octave; default 0.";
|
|
const char doc207[] = "(edit 'function)\n"
|
|
"Calls the Lisp tree editor to allow you to edit a function definition.";
|
|
const char doc208[] = "(pprint item [str])\n"
|
|
"Prints its argument, using the pretty printer, to display it formatted in a structured way.\n"
|
|
"If str is specified it prints to the specified stream. It returns no value.";
|
|
const char doc209[] = "(pprintall [str])\n"
|
|
"Pretty-prints the definition of every function and variable defined in the uLisp workspace.\n"
|
|
"If str is specified it prints to the specified stream. It returns no value.";
|
|
const char doc210[] = "(require 'symbol)\n"
|
|
"Loads the definition of a function defined with defun, or a variable defined with defvar, from the Lisp Library.\n"
|
|
"It returns t if it was loaded, or nil if the symbol is already defined or isn't defined in the Lisp Library.";
|
|
const char doc211[] = "(list-library)\n"
|
|
"Prints a list of the functions defined in the List Library.";
|
|
const char doc212[] = "(? item)\n"
|
|
"Prints the documentation string of a built-in or user-defined function.";
|
|
const char doc213[] = "(documentation 'symbol [type])\n"
|
|
"Returns the documentation string of a built-in or user-defined function. The type argument is ignored.";
|
|
const char doc214[] = "(apropos item)\n"
|
|
"Prints the user-defined and built-in functions whose names contain the specified string or symbol.";
|
|
const char doc215[] = "(apropos-list item)\n"
|
|
"Returns a list of user-defined and built-in functions whose names contain the specified string or symbol.";
|
|
const char doc216[] = "(unwind-protect form1 [forms]*)\n"
|
|
"Evaluates form1 and forms in order and returns the value of form1,\n"
|
|
"but guarantees to evaluate forms even if an error occurs in form1.";
|
|
const char doc217[] = "(ignore-errors [forms]*)\n"
|
|
"Evaluates forms ignoring errors.";
|
|
const char doc218[] = "(error controlstring [arguments]*)\n"
|
|
"Signals an error. The message is printed by format using the controlstring and arguments.";
|
|
const char doc219[] = "(directory)\n"
|
|
"Returns a list of the filenames of the files on the SD card.";
|
|
const char doc220[] = "(with-client (str [address port]) form*)\n"
|
|
"Evaluates the forms with str bound to a wifi-stream.";
|
|
const char doc221[] = "(available stream)\n"
|
|
"Returns the number of bytes available for reading from the wifi-stream, or zero if no bytes are available.";
|
|
const char doc222[] = "(wifi-server)\n"
|
|
"Starts a Wi-Fi server running. It returns nil.";
|
|
const char doc223[] = "(wifi-softap ssid [password channel hidden])\n"
|
|
"Set up a soft access point to establish a Wi-Fi network.\n"
|
|
"Returns the IP address as a string or nil if unsuccessful.";
|
|
const char doc224[] = "(connected stream)\n"
|
|
"Returns t or nil to indicate if the client on stream is connected.";
|
|
const char doc225[] = "(wifi-localip)\n"
|
|
"Returns the IP address of the local network as a string.";
|
|
const char doc226[] = "(wifi-connect [ssid pass])\n"
|
|
"Connects to the Wi-Fi network ssid using password pass. It returns the IP address as a string.";
|
|
const char doc227[] = "(with-gfx (str) form*)\n"
|
|
"Evaluates the forms with str bound to an gfx-stream so you can print text\n"
|
|
"to the graphics display using the standard uLisp print commands.";
|
|
const char doc228[] = "(draw-pixel x y [colour])\n"
|
|
"Draws a pixel at coordinates (x,y) in colour, or white if omitted.";
|
|
const char doc229[] = "(draw-line x0 y0 x1 y1 [colour])\n"
|
|
"Draws a line from (x0,y0) to (x1,y1) in colour, or white if omitted.";
|
|
const char doc230[] = "(draw-rect x y w h [colour])\n"
|
|
"Draws an outline rectangle with its top left corner at (x,y), with width w,\n"
|
|
"and with height h. The outline is drawn in colour, or white if omitted.";
|
|
const char doc231[] = "(fill-rect x y w h [colour])\n"
|
|
"Draws a filled rectangle with its top left corner at (x,y), with width w,\n"
|
|
"and with height h. The outline is drawn in colour, or white if omitted.";
|
|
const char doc232[] = "(draw-circle x y r [colour])\n"
|
|
"Draws an outline circle with its centre at (x, y) and with radius r.\n"
|
|
"The circle is drawn in colour, or white if omitted.";
|
|
const char doc233[] = "(fill-circle x y r [colour])\n"
|
|
"Draws a filled circle with its centre at (x, y) and with radius r.\n"
|
|
"The circle is drawn in colour, or white if omitted.";
|
|
const char doc234[] = "(draw-round-rect x y w h radius [colour])\n"
|
|
"Draws an outline rounded rectangle with its top left corner at (x,y), with width w,\n"
|
|
"height h, and corner radius radius. The outline is drawn in colour, or white if omitted.";
|
|
const char doc235[] = "(fill-round-rect x y w h radius [colour])\n"
|
|
"Draws a filled rounded rectangle with its top left corner at (x,y), with width w,\n"
|
|
"height h, and corner radius radius. The outline is drawn in colour, or white if omitted.";
|
|
const char doc236[] = "(draw-triangle x0 y0 x1 y1 x2 y2 [colour])\n"
|
|
"Draws an outline triangle between (x1,y1), (x2,y2), and (x3,y3).\n"
|
|
"The outline is drawn in colour, or white if omitted.";
|
|
const char doc237[] = "(fill-triangle x0 y0 x1 y1 x2 y2 [colour])\n"
|
|
"Draws a filled triangle between (x1,y1), (x2,y2), and (x3,y3).\n"
|
|
"The outline is drawn in colour, or white if omitted.";
|
|
const char doc238[] = "(draw-char x y char [colour background size])\n"
|
|
"Draws the character char with its top left corner at (x,y).\n"
|
|
"The character is drawn in a 5 x 7 pixel font in colour against background,\n"
|
|
"which default to white and black respectively.\n"
|
|
"The character can optionally be scaled by size.";
|
|
const char doc239[] = "(set-cursor x y)\n"
|
|
"Sets the start point for text plotting to (x, y).";
|
|
const char doc240[] = "(set-text-color colour [background])\n"
|
|
"Sets the text colour for text plotted using (with-gfx ...).";
|
|
const char doc241[] = "(set-text-size scale)\n"
|
|
"Scales text by the specified size, default 1.";
|
|
const char doc242[] = "(set-text-wrap boolean)\n"
|
|
"Specified whether text wraps at the right-hand edge of the display; the default is t.";
|
|
const char doc243[] = "(fill-screen [colour])\n"
|
|
"Fills or clears the screen with colour, default black.";
|
|
const char doc244[] = "(set-rotation option)\n"
|
|
"Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3.";
|
|
const char doc245[] = "(invert-display boolean)\n"
|
|
"Mirror-images the display.";
|
|
|
|
// Built-in symbol lookup table
|
|
const tbl_entry_t lookup_table[] = {
|
|
{ string0, NULL, 0000, doc0 },
|
|
{ string1, NULL, 0000, doc1 },
|
|
{ string2, NULL, 0000, doc2 },
|
|
{ string3, NULL, 0000, doc3 },
|
|
{ string4, NULL, 0000, doc4 },
|
|
{ string5, NULL, 0000, NULL },
|
|
{ string6, NULL, 0000, NULL },
|
|
{ string7, NULL, 0000, NULL },
|
|
{ string8, NULL, 0000, NULL },
|
|
{ string9, NULL, 0000, NULL },
|
|
{ string10, NULL, 0000, NULL },
|
|
{ string11, NULL, 0000, NULL },
|
|
{ string12, NULL, 0000, doc12 },
|
|
{ string13, NULL, 0017, doc13 },
|
|
{ string14, NULL, 0017, doc14 },
|
|
{ string15, NULL, 0017, doc15 },
|
|
{ string16, NULL, 0017, NULL },
|
|
{ string17, NULL, 0007, NULL },
|
|
{ string18, sp_quote, 0311, NULL },
|
|
{ string19, sp_defun, 0327, doc19 },
|
|
{ string20, sp_defvar, 0313, doc20 },
|
|
{ string21, sp_defcode, 0307, doc21 },
|
|
{ string22, fn_eq, 0222, doc22 },
|
|
{ string23, fn_car, 0211, doc23 },
|
|
{ string24, fn_car, 0211, doc24 },
|
|
{ string25, fn_cdr, 0211, doc25 },
|
|
{ string26, fn_cdr, 0211, doc26 },
|
|
{ string27, fn_nth, 0222, doc27 },
|
|
{ string28, fn_aref, 0227, doc28 },
|
|
{ string29, fn_char, 0222, doc29 },
|
|
{ string30, fn_stringfn, 0211, doc30 },
|
|
{ string31, fn_pinmode, 0222, doc31 },
|
|
{ string32, fn_digitalwrite, 0222, doc32 },
|
|
{ string33, fn_analogread, 0211, doc33 },
|
|
{ string34, fn_analogreference, 0211, doc34 },
|
|
{ string35, fn_register, 0212, doc35 },
|
|
{ string36, fn_format, 0227, doc36 },
|
|
{ string37, sp_or, 0307, doc37 },
|
|
{ string38, sp_setq, 0327, doc38 },
|
|
{ string39, sp_loop, 0307, doc39 },
|
|
{ string40, sp_push, 0322, doc40 },
|
|
{ string41, sp_pop, 0311, doc41 },
|
|
{ string42, sp_incf, 0312, doc42 },
|
|
{ string43, sp_decf, 0312, doc43 },
|
|
{ string44, sp_setf, 0327, doc44 },
|
|
{ string45, sp_dolist, 0317, doc45 },
|
|
{ string46, sp_dotimes, 0317, doc46 },
|
|
{ string47, sp_do, 0327, doc47 },
|
|
{ string48, sp_dostar, 0317, doc48 },
|
|
{ string49, sp_trace, 0301, doc49 },
|
|
{ string50, sp_untrace, 0301, doc50 },
|
|
{ string51, sp_formillis, 0317, doc51 },
|
|
{ string52, sp_time, 0311, doc52 },
|
|
{ string53, sp_withoutputtostring, 0317, doc53 },
|
|
{ string54, sp_withserial, 0317, doc54 },
|
|
{ string55, sp_withi2c, 0317, doc55 },
|
|
{ string56, sp_withspi, 0317, doc56 },
|
|
{ string57, sp_withsdcard, 0327, doc57 },
|
|
{ string58, tf_progn, 0107, doc58 },
|
|
{ string59, tf_if, 0123, doc59 },
|
|
{ string60, tf_cond, 0107, doc60 },
|
|
{ string61, tf_when, 0117, doc61 },
|
|
{ string62, tf_unless, 0117, doc62 },
|
|
{ string63, tf_case, 0117, doc63 },
|
|
{ string64, tf_and, 0107, doc64 },
|
|
{ string65, fn_not, 0211, doc65 },
|
|
{ string66, fn_not, 0211, doc66 },
|
|
{ string67, fn_cons, 0222, doc67 },
|
|
{ string68, fn_atom, 0211, doc68 },
|
|
{ string69, fn_listp, 0211, doc69 },
|
|
{ string70, fn_consp, 0211, doc70 },
|
|
{ string71, fn_symbolp, 0211, doc71 },
|
|
{ string72, fn_arrayp, 0211, doc72 },
|
|
{ string73, fn_boundp, 0211, doc73 },
|
|
{ string74, fn_keywordp, 0211, doc74 },
|
|
{ string75, fn_setfn, 0227, doc75 },
|
|
{ string76, fn_streamp, 0211, doc76 },
|
|
{ string77, fn_equal, 0222, doc77 },
|
|
{ string78, fn_caar, 0211, doc78 },
|
|
{ string79, fn_cadr, 0211, doc79 },
|
|
{ string80, fn_cadr, 0211, doc80 },
|
|
{ string81, fn_cdar, 0211, doc81 },
|
|
{ string82, fn_cddr, 0211, doc82 },
|
|
{ string83, fn_caaar, 0211, doc83 },
|
|
{ string84, fn_caadr, 0211, doc84 },
|
|
{ string85, fn_cadar, 0211, doc85 },
|
|
{ string86, fn_caddr, 0211, doc86 },
|
|
{ string87, fn_caddr, 0211, doc87 },
|
|
{ string88, fn_cdaar, 0211, doc88 },
|
|
{ string89, fn_cdadr, 0211, doc89 },
|
|
{ string90, fn_cddar, 0211, doc90 },
|
|
{ string91, fn_cdddr, 0211, doc91 },
|
|
{ string92, fn_length, 0211, doc92 },
|
|
{ string93, fn_arraydimensions, 0211, doc93 },
|
|
{ string94, fn_list, 0207, doc94 },
|
|
{ string95, fn_copylist, 0211, doc95 },
|
|
{ string96, fn_makearray, 0215, doc96 },
|
|
{ string97, fn_reverse, 0211, doc97 },
|
|
{ string98, fn_assoc, 0224, doc98 },
|
|
{ string99, fn_member, 0224, doc99 },
|
|
{ string100, fn_apply, 0227, doc100 },
|
|
{ string101, fn_funcall, 0217, doc101 },
|
|
{ string102, fn_append, 0207, doc102 },
|
|
{ string103, fn_mapc, 0227, doc103 },
|
|
{ string104, fn_mapl, 0227, doc104 },
|
|
{ string105, fn_mapcar, 0227, doc105 },
|
|
{ string106, fn_mapcan, 0227, doc106 },
|
|
{ string107, fn_maplist, 0227, doc107 },
|
|
{ string108, fn_mapcon, 0227, doc108 },
|
|
{ string109, fn_add, 0207, doc109 },
|
|
{ string110, fn_subtract, 0217, doc110 },
|
|
{ string111, fn_multiply, 0207, doc111 },
|
|
{ string112, fn_divide, 0217, doc112 },
|
|
{ string113, fn_mod, 0222, doc113 },
|
|
{ string114, fn_rem, 0222, doc114 },
|
|
{ string115, fn_oneplus, 0211, doc115 },
|
|
{ string116, fn_oneminus, 0211, doc116 },
|
|
{ string117, fn_abs, 0211, doc117 },
|
|
{ string118, fn_random, 0211, doc118 },
|
|
{ string119, fn_maxfn, 0217, doc119 },
|
|
{ string120, fn_minfn, 0217, doc120 },
|
|
{ string121, fn_noteq, 0217, doc121 },
|
|
{ string122, fn_numeq, 0217, doc122 },
|
|
{ string123, fn_less, 0217, doc123 },
|
|
{ string124, fn_lesseq, 0217, doc124 },
|
|
{ string125, fn_greater, 0217, doc125 },
|
|
{ string126, fn_greatereq, 0217, doc126 },
|
|
{ string127, fn_plusp, 0211, doc127 },
|
|
{ string128, fn_minusp, 0211, doc128 },
|
|
{ string129, fn_zerop, 0211, doc129 },
|
|
{ string130, fn_oddp, 0211, doc130 },
|
|
{ string131, fn_evenp, 0211, doc131 },
|
|
{ string132, fn_integerp, 0211, doc132 },
|
|
{ string133, fn_numberp, 0211, doc133 },
|
|
{ string134, fn_floatfn, 0211, doc134 },
|
|
{ string135, fn_floatp, 0211, doc135 },
|
|
{ string136, fn_sin, 0211, doc136 },
|
|
{ string137, fn_cos, 0211, doc137 },
|
|
{ string138, fn_tan, 0211, doc138 },
|
|
{ string139, fn_asin, 0211, doc139 },
|
|
{ string140, fn_acos, 0211, doc140 },
|
|
{ string141, fn_atan, 0212, doc141 },
|
|
{ string142, fn_sinh, 0211, doc142 },
|
|
{ string143, fn_cosh, 0211, doc143 },
|
|
{ string144, fn_tanh, 0211, doc144 },
|
|
{ string145, fn_exp, 0211, doc145 },
|
|
{ string146, fn_sqrt, 0211, doc146 },
|
|
{ string147, fn_log, 0212, doc147 },
|
|
{ string148, fn_expt, 0222, doc148 },
|
|
{ string149, fn_ceiling, 0212, doc149 },
|
|
{ string150, fn_floor, 0212, doc150 },
|
|
{ string151, fn_truncate, 0212, doc151 },
|
|
{ string152, fn_round, 0212, doc152 },
|
|
{ string153, fn_charcode, 0211, doc153 },
|
|
{ string154, fn_codechar, 0211, doc154 },
|
|
{ string155, fn_characterp, 0211, doc155 },
|
|
{ string156, fn_stringp, 0211, doc156 },
|
|
{ string157, fn_stringeq, 0222, doc157 },
|
|
{ string158, fn_stringless, 0222, doc158 },
|
|
{ string159, fn_stringgreater, 0222, doc159 },
|
|
{ string160, fn_stringnoteq, 0222, doc160 },
|
|
{ string161, fn_stringlesseq, 0222, doc161 },
|
|
{ string162, fn_stringgreatereq, 0222, doc162 },
|
|
{ string163, fn_sort, 0222, doc163 },
|
|
{ string164, fn_concatenate, 0217, doc164 },
|
|
{ string165, fn_subseq, 0223, doc165 },
|
|
{ string166, fn_search, 0224, doc166 },
|
|
{ string167, fn_readfromstring, 0211, doc167 },
|
|
{ string168, fn_princtostring, 0211, doc168 },
|
|
{ string169, fn_prin1tostring, 0211, doc169 },
|
|
{ string170, fn_logand, 0207, doc170 },
|
|
{ string171, fn_logior, 0207, doc171 },
|
|
{ string172, fn_logxor, 0207, doc172 },
|
|
{ string173, fn_lognot, 0211, doc173 },
|
|
{ string174, fn_ash, 0222, doc174 },
|
|
{ string175, fn_logbitp, 0222, doc175 },
|
|
{ string176, fn_eval, 0211, doc176 },
|
|
{ string177, fn_return, 0201, doc177 },
|
|
{ string178, fn_globals, 0200, doc178 },
|
|
{ string179, fn_locals, 0200, doc179 },
|
|
{ string180, fn_makunbound, 0211, doc180 },
|
|
{ string181, fn_break, 0200, doc181 },
|
|
{ string182, fn_read, 0201, doc182 },
|
|
{ string183, fn_prin1, 0212, doc183 },
|
|
{ string184, fn_print, 0212, doc184 },
|
|
{ string185, fn_princ, 0212, doc185 },
|
|
{ string186, fn_terpri, 0201, doc186 },
|
|
{ string187, fn_readbyte, 0202, doc187 },
|
|
{ string188, fn_readline, 0201, doc188 },
|
|
{ string189, fn_writebyte, 0212, doc189 },
|
|
{ string190, fn_writestring, 0212, doc190 },
|
|
{ string191, fn_writeline, 0212, doc191 },
|
|
{ string192, fn_restarti2c, 0212, doc192 },
|
|
{ string193, fn_gc, 0201, doc193 },
|
|
{ string194, fn_room, 0200, doc194 },
|
|
{ string195, fn_backtrace, 0201, doc195 },
|
|
{ string196, fn_saveimage, 0201, doc196 },
|
|
{ string197, fn_loadimage, 0201, doc197 },
|
|
{ string198, fn_cls, 0200, doc198 },
|
|
{ string199, fn_digitalread, 0211, doc199 },
|
|
{ string200, fn_analogreadresolution, 0211, doc200 },
|
|
{ string201, fn_analogwrite, 0222, doc201 },
|
|
{ string202, fn_analogwriteresolution, 0211, doc202 },
|
|
{ string203, fn_delay, 0211, doc203 },
|
|
{ string204, fn_millis, 0200, doc204 },
|
|
{ string205, fn_sleep, 0201, doc205 },
|
|
{ string206, fn_note, 0203, doc206 },
|
|
{ string207, fn_edit, 0211, doc207 },
|
|
{ string208, fn_pprint, 0212, doc208 },
|
|
{ string209, fn_pprintall, 0201, doc209 },
|
|
{ string210, fn_require, 0211, doc210 },
|
|
{ string211, fn_listlibrary, 0200, doc211 },
|
|
{ string212, sp_help, 0311, doc212 },
|
|
{ string213, fn_documentation, 0212, doc213 },
|
|
{ string214, fn_apropos, 0211, doc214 },
|
|
{ string215, fn_aproposlist, 0211, doc215 },
|
|
{ string216, sp_unwindprotect, 0307, doc216 },
|
|
{ string217, sp_ignoreerrors, 0307, doc217 },
|
|
{ string218, sp_error, 0317, doc218 },
|
|
{ string219, fn_directory, 0200, doc219 },
|
|
{ string220, sp_withclient, 0317, doc220 },
|
|
{ string221, fn_available, 0211, doc221 },
|
|
{ string222, fn_wifiserver, 0200, doc222 },
|
|
{ string223, fn_wifisoftap, 0204, doc223 },
|
|
{ string224, fn_connected, 0211, doc224 },
|
|
{ string225, fn_wifilocalip, 0200, doc225 },
|
|
{ string226, fn_wificonnect, 0203, doc226 },
|
|
{ string227, sp_withgfx, 0317, doc227 },
|
|
{ string228, fn_drawpixel, 0223, doc228 },
|
|
{ string229, fn_drawline, 0245, doc229 },
|
|
{ string230, fn_drawrect, 0245, doc230 },
|
|
{ string231, fn_fillrect, 0245, doc231 },
|
|
{ string232, fn_drawcircle, 0234, doc232 },
|
|
{ string233, fn_fillcircle, 0234, doc233 },
|
|
{ string234, fn_drawroundrect, 0256, doc234 },
|
|
{ string235, fn_fillroundrect, 0256, doc235 },
|
|
{ string236, fn_drawtriangle, 0267, doc236 },
|
|
{ string237, fn_filltriangle, 0267, doc237 },
|
|
{ string238, fn_drawchar, 0236, doc238 },
|
|
{ string239, fn_setcursor, 0222, doc239 },
|
|
{ string240, fn_settextcolor, 0212, doc240 },
|
|
{ string241, fn_settextsize, 0211, doc241 },
|
|
{ string242, fn_settextwrap, 0211, doc242 },
|
|
{ string243, fn_fillscreen, 0201, doc243 },
|
|
{ string244, fn_setrotation, 0211, doc244 },
|
|
{ string245, fn_invertdisplay, 0211, doc245 },
|
|
{ string246, (fn_ptr_type)LED_BUILTIN, 0, NULL },
|
|
{ string247, (fn_ptr_type)HIGH, DIGITALWRITE, NULL },
|
|
{ string248, (fn_ptr_type)LOW, DIGITALWRITE, NULL },
|
|
#if defined(CPU_ATSAMD21)
|
|
{ string249, (fn_ptr_type)INPUT, PINMODE, NULL },
|
|
{ string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL },
|
|
{ string251, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL },
|
|
{ string252, (fn_ptr_type)OUTPUT, PINMODE, NULL },
|
|
{ string253, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE, NULL },
|
|
{ string254, (fn_ptr_type)AR_INTERNAL1V0, ANALOGREFERENCE, NULL },
|
|
{ string255, (fn_ptr_type)AR_INTERNAL1V65, ANALOGREFERENCE, NULL },
|
|
{ string256, (fn_ptr_type)AR_INTERNAL2V23, ANALOGREFERENCE, NULL },
|
|
{ string257, (fn_ptr_type)AR_EXTERNAL, ANALOGREFERENCE, NULL },
|
|
{ string258, (fn_ptr_type)&PORT->Group[0].DIR.reg, REGISTER, NULL },
|
|
{ string259, (fn_ptr_type)&PORT->Group[0].DIRCLR.reg, REGISTER, NULL },
|
|
{ string260, (fn_ptr_type)&PORT->Group[0].DIRSET.reg, REGISTER, NULL },
|
|
{ string261, (fn_ptr_type)&PORT->Group[0].DIRTGL.reg, REGISTER, NULL },
|
|
{ string262, (fn_ptr_type)&PORT->Group[0].OUT.reg, REGISTER, NULL },
|
|
{ string263, (fn_ptr_type)&PORT->Group[0].OUTCLR.reg, REGISTER, NULL },
|
|
{ string264, (fn_ptr_type)&PORT->Group[0].OUTSET.reg, REGISTER, NULL },
|
|
{ string265, (fn_ptr_type)&PORT->Group[0].OUTTGL.reg, REGISTER, NULL },
|
|
{ string266, (fn_ptr_type)&PORT->Group[0].IN.reg, REGISTER, NULL },
|
|
{ string267, (fn_ptr_type)&PORT->Group[1].DIR.reg, REGISTER, NULL },
|
|
{ string268, (fn_ptr_type)&PORT->Group[1].DIRCLR.reg, REGISTER, NULL },
|
|
{ string269, (fn_ptr_type)&PORT->Group[1].DIRSET.reg, REGISTER, NULL },
|
|
{ string270, (fn_ptr_type)&PORT->Group[1].DIRTGL.reg, REGISTER, NULL },
|
|
{ string271, (fn_ptr_type)&PORT->Group[1].OUT.reg, REGISTER, NULL },
|
|
{ string272, (fn_ptr_type)&PORT->Group[1].OUTCLR.reg, REGISTER, NULL },
|
|
{ string273, (fn_ptr_type)&PORT->Group[1].OUTSET.reg, REGISTER, NULL },
|
|
{ string274, (fn_ptr_type)&PORT->Group[1].OUTTGL.reg, REGISTER, NULL },
|
|
{ string275, (fn_ptr_type)&PORT->Group[1].IN.reg, REGISTER, NULL },
|
|
#elif defined(CPU_ATSAMD51)
|
|
{ string249, (fn_ptr_type)INPUT, PINMODE, NULL },
|
|
{ string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL },
|
|
{ string251, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL },
|
|
{ string252, (fn_ptr_type)OUTPUT, PINMODE, NULL },
|
|
{ string253, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE, NULL },
|
|
{ string254, (fn_ptr_type)AR_INTERNAL1V0, ANALOGREFERENCE, NULL },
|
|
{ string255, (fn_ptr_type)AR_INTERNAL1V1, ANALOGREFERENCE, NULL },
|
|
{ string256, (fn_ptr_type)AR_INTERNAL1V2, ANALOGREFERENCE, NULL },
|
|
{ string257, (fn_ptr_type)AR_INTERNAL1V25, ANALOGREFERENCE, NULL },
|
|
{ string258, (fn_ptr_type)AR_INTERNAL1V65, ANALOGREFERENCE, NULL },
|
|
{ string259, (fn_ptr_type)AR_INTERNAL2V0, ANALOGREFERENCE, NULL },
|
|
{ string260, (fn_ptr_type)AR_INTERNAL2V2, ANALOGREFERENCE, NULL },
|
|
{ string261, (fn_ptr_type)AR_INTERNAL2V23, ANALOGREFERENCE, NULL },
|
|
{ string262, (fn_ptr_type)AR_INTERNAL2V4, ANALOGREFERENCE, NULL },
|
|
{ string263, (fn_ptr_type)AR_INTERNAL2V5, ANALOGREFERENCE, NULL },
|
|
{ string264, (fn_ptr_type)AR_EXTERNAL, ANALOGREFERENCE, NULL },
|
|
{ string265, (fn_ptr_type)&PORT->Group[0].DIR.reg, REGISTER, NULL },
|
|
{ string266, (fn_ptr_type)&PORT->Group[0].DIRCLR.reg, REGISTER, NULL },
|
|
{ string267, (fn_ptr_type)&PORT->Group[0].DIRSET.reg, REGISTER, NULL },
|
|
{ string268, (fn_ptr_type)&PORT->Group[0].DIRTGL.reg, REGISTER, NULL },
|
|
{ string269, (fn_ptr_type)&PORT->Group[0].OUT.reg, REGISTER, NULL },
|
|
{ string270, (fn_ptr_type)&PORT->Group[0].OUTCLR.reg, REGISTER, NULL },
|
|
{ string271, (fn_ptr_type)&PORT->Group[0].OUTSET.reg, REGISTER, NULL },
|
|
{ string272, (fn_ptr_type)&PORT->Group[0].OUTTGL.reg, REGISTER, NULL },
|
|
{ string273, (fn_ptr_type)&PORT->Group[0].IN.reg, REGISTER, NULL },
|
|
{ string274, (fn_ptr_type)&PORT->Group[1].DIR.reg, REGISTER, NULL },
|
|
{ string275, (fn_ptr_type)&PORT->Group[1].DIRCLR.reg, REGISTER, NULL },
|
|
{ string276, (fn_ptr_type)&PORT->Group[1].DIRSET.reg, REGISTER, NULL },
|
|
{ string277, (fn_ptr_type)&PORT->Group[1].DIRTGL.reg, REGISTER, NULL },
|
|
{ string278, (fn_ptr_type)&PORT->Group[1].OUT.reg, REGISTER, NULL },
|
|
{ string279, (fn_ptr_type)&PORT->Group[1].OUTCLR.reg, REGISTER, NULL },
|
|
{ string280, (fn_ptr_type)&PORT->Group[1].OUTSET.reg, REGISTER, NULL },
|
|
{ string281, (fn_ptr_type)&PORT->Group[1].OUTTGL.reg, REGISTER, NULL },
|
|
{ string282, (fn_ptr_type)&PORT->Group[1].IN.reg, REGISTER, NULL },
|
|
#elif defined(CPU_NRF51822)
|
|
{ string249, (fn_ptr_type)INPUT, PINMODE, NULL },
|
|
{ string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL },
|
|
{ string251, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL },
|
|
{ string252, (fn_ptr_type)OUTPUT, PINMODE, NULL },
|
|
{ string253, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE, NULL },
|
|
{ string254, (fn_ptr_type)AR_VBG, ANALOGREFERENCE, NULL },
|
|
{ string255, (fn_ptr_type)AR_SUPPLY_ONE_HALF, ANALOGREFERENCE, NULL },
|
|
{ string256, (fn_ptr_type)AR_SUPPLY_ONE_THIRD, ANALOGREFERENCE, NULL },
|
|
{ string257, (fn_ptr_type)AR_EXT0, ANALOGREFERENCE, NULL },
|
|
{ string258, (fn_ptr_type)AR_EXT1, ANALOGREFERENCE, NULL },
|
|
{ string259, (fn_ptr_type)&NRF_GPIO->OUT, REGISTER, NULL },
|
|
{ string260, (fn_ptr_type)&NRF_GPIO->OUTSET, REGISTER, NULL },
|
|
{ string261, (fn_ptr_type)&NRF_GPIO->OUTCLR, REGISTER, NULL },
|
|
{ string262, (fn_ptr_type)&NRF_GPIO->IN, REGISTER, NULL },
|
|
{ string263, (fn_ptr_type)&NRF_GPIO->DIR, REGISTER, NULL },
|
|
{ string264, (fn_ptr_type)&NRF_GPIO->DIRSET, REGISTER, NULL },
|
|
{ string265, (fn_ptr_type)&NRF_GPIO->DIRCLR, REGISTER, NULL },
|
|
#elif defined(CPU_NRF52840)
|
|
{ string249, (fn_ptr_type)INPUT, PINMODE, NULL },
|
|
{ string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL },
|
|
{ string251, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL },
|
|
{ string252, (fn_ptr_type)OUTPUT, PINMODE, NULL },
|
|
{ string253, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE, NULL },
|
|
{ string254, (fn_ptr_type)AR_INTERNAL, ANALOGREFERENCE, NULL },
|
|
{ string255, (fn_ptr_type)AR_INTERNAL_3_0, ANALOGREFERENCE, NULL },
|
|
{ string256, (fn_ptr_type)AR_INTERNAL_2_4, ANALOGREFERENCE, NULL },
|
|
{ string257, (fn_ptr_type)AR_INTERNAL_1_8, ANALOGREFERENCE, NULL },
|
|
{ string258, (fn_ptr_type)AR_INTERNAL_1_2, ANALOGREFERENCE, NULL },
|
|
{ string259, (fn_ptr_type)AR_VDD4, ANALOGREFERENCE, NULL },
|
|
{ string260, (fn_ptr_type)&NRF_P0->OUT, REGISTER, NULL },
|
|
{ string261, (fn_ptr_type)&NRF_P0->OUTSET, REGISTER, NULL },
|
|
{ string262, (fn_ptr_type)&NRF_P0->OUTCLR, REGISTER, NULL },
|
|
{ string263, (fn_ptr_type)&NRF_P0->IN, REGISTER, NULL },
|
|
{ string264, (fn_ptr_type)&NRF_P0->DIR, REGISTER, NULL },
|
|
{ string265, (fn_ptr_type)&NRF_P0->DIRSET, REGISTER, NULL },
|
|
{ string266, (fn_ptr_type)&NRF_P0->DIRCLR, REGISTER, NULL },
|
|
{ string267, (fn_ptr_type)&NRF_P1->OUT, REGISTER, NULL },
|
|
{ string268, (fn_ptr_type)&NRF_P1->OUTSET, REGISTER, NULL },
|
|
{ string269, (fn_ptr_type)&NRF_P1->OUTCLR, REGISTER, NULL },
|
|
{ string270, (fn_ptr_type)&NRF_P1->IN, REGISTER, NULL },
|
|
{ string271, (fn_ptr_type)&NRF_P1->DIR, REGISTER, NULL },
|
|
{ string272, (fn_ptr_type)&NRF_P1->DIRSET, REGISTER, NULL },
|
|
{ string273, (fn_ptr_type)&NRF_P1->DIRCLR, REGISTER, NULL },
|
|
#elif defined(CPU_NRF52833)
|
|
{ string249, (fn_ptr_type)INPUT, PINMODE, NULL },
|
|
{ string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL },
|
|
{ string251, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL },
|
|
{ string252, (fn_ptr_type)OUTPUT, PINMODE, NULL },
|
|
{ string253, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE, NULL },
|
|
{ string254, (fn_ptr_type)AR_INTERNAL, ANALOGREFERENCE, NULL },
|
|
{ string255, (fn_ptr_type)AR_VDD4, ANALOGREFERENCE, NULL },
|
|
{ string256, (fn_ptr_type)&NRF_P0->OUT, REGISTER, NULL },
|
|
{ string257, (fn_ptr_type)&NRF_P0->OUTSET, REGISTER, NULL },
|
|
{ string258, (fn_ptr_type)&NRF_P0->OUTCLR, REGISTER, NULL },
|
|
{ string259, (fn_ptr_type)&NRF_P0->IN, REGISTER, NULL },
|
|
{ string260, (fn_ptr_type)&NRF_P0->DIR, REGISTER, NULL },
|
|
{ string261, (fn_ptr_type)&NRF_P0->DIRSET, REGISTER, NULL },
|
|
{ string262, (fn_ptr_type)&NRF_P0->DIRCLR, REGISTER, NULL },
|
|
{ string263, (fn_ptr_type)&NRF_P1->OUT, REGISTER, NULL },
|
|
{ string264, (fn_ptr_type)&NRF_P1->OUTSET, REGISTER, NULL },
|
|
{ string265, (fn_ptr_type)&NRF_P1->OUTCLR, REGISTER, NULL },
|
|
{ string266, (fn_ptr_type)&NRF_P1->IN, REGISTER, NULL },
|
|
{ string267, (fn_ptr_type)&NRF_P1->DIR, REGISTER, NULL },
|
|
{ string268, (fn_ptr_type)&NRF_P1->DIRSET, REGISTER, NULL },
|
|
{ string269, (fn_ptr_type)&NRF_P1->DIRCLR, REGISTER, NULL },
|
|
#elif defined(CPU_iMXRT1062)
|
|
{ string249, (fn_ptr_type)INPUT, PINMODE, NULL },
|
|
{ string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL },
|
|
{ string251, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL },
|
|
{ string252, (fn_ptr_type)OUTPUT, PINMODE, NULL },
|
|
{ string253, (fn_ptr_type)OUTPUT_OPENDRAIN, PINMODE, NULL },
|
|
#elif defined(CPU_MAX32620)
|
|
{ string249, (fn_ptr_type)INPUT, PINMODE, NULL },
|
|
{ string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL },
|
|
{ string251, (fn_ptr_type)OUTPUT, PINMODE, NULL },
|
|
{ string252, (fn_ptr_type)DEFAULT, ANALOGREFERENCE, NULL },
|
|
{ string253, (fn_ptr_type)EXTERNAL, ANALOGREFERENCE, NULL },
|
|
#elif defined(CPU_RP2040)
|
|
{ string249, (fn_ptr_type)INPUT, PINMODE, NULL },
|
|
{ string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL },
|
|
{ string251, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL },
|
|
{ string252, (fn_ptr_type)OUTPUT, PINMODE, NULL },
|
|
{ string253, (fn_ptr_type)(SIO_BASE+SIO_GPIO_IN_OFFSET), REGISTER, NULL },
|
|
{ string254, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_OFFSET), REGISTER, NULL },
|
|
{ string255, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_SET_OFFSET), REGISTER, NULL },
|
|
{ string256, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_CLR_OFFSET), REGISTER, NULL },
|
|
{ string257, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_XOR_OFFSET), REGISTER, NULL },
|
|
{ string258, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_OFFSET), REGISTER, NULL },
|
|
{ string259, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_SET_OFFSET), REGISTER, NULL },
|
|
{ string260, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_CLR_OFFSET), REGISTER, NULL },
|
|
{ string261, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_XOR_OFFSET), REGISTER, NULL },
|
|
#elif defined(CPU_RP2350)
|
|
{ string249, (fn_ptr_type)INPUT, PINMODE, NULL },
|
|
{ string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL },
|
|
{ string251, (fn_ptr_type)INPUT_PULLDOWN, PINMODE, NULL },
|
|
{ string252, (fn_ptr_type)OUTPUT, PINMODE, NULL },
|
|
{ string253, (fn_ptr_type)(SIO_BASE+SIO_GPIO_IN_OFFSET), REGISTER, NULL },
|
|
{ string254, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_OFFSET), REGISTER, NULL },
|
|
{ string255, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_SET_OFFSET), REGISTER, NULL },
|
|
{ string256, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_CLR_OFFSET), REGISTER, NULL },
|
|
{ string257, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_XOR_OFFSET), REGISTER, NULL },
|
|
{ string258, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_OFFSET), REGISTER, NULL },
|
|
{ string259, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_SET_OFFSET), REGISTER, NULL },
|
|
{ string260, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_CLR_OFFSET), REGISTER, NULL },
|
|
{ string261, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_XOR_OFFSET), REGISTER, NULL },
|
|
#elif defined(CPU_RA4M1)
|
|
{ string249, (fn_ptr_type)INPUT, PINMODE, NULL },
|
|
{ string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL },
|
|
{ string251, (fn_ptr_type)OUTPUT, PINMODE, NULL },
|
|
{ string252, (fn_ptr_type)OUTPUT_OPENDRAIN, PINMODE, NULL },
|
|
{ string253, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE, NULL },
|
|
{ string254, (fn_ptr_type)AR_INTERNAL, ANALOGREFERENCE, NULL },
|
|
{ string255, (fn_ptr_type)AR_EXTERNAL, ANALOGREFERENCE, NULL },
|
|
#endif
|
|
};
|
|
|
|
#if !defined(extensions)
|
|
// Table cross-reference functions
|
|
|
|
tbl_entry_t *tables[] = {lookup_table, NULL};
|
|
const unsigned int tablesizes[] = { arraysize(lookup_table), 0 };
|
|
|
|
const tbl_entry_t *table (int n) {
|
|
return tables[n];
|
|
}
|
|
|
|
unsigned int tablesize (int n) {
|
|
return tablesizes[n];
|
|
}
|
|
#endif
|
|
|
|
// Table lookup functions
|
|
|
|
/*
|
|
lookupbuiltin - looks up a string in lookup_table[], and returns the index of its entry, or ENDFUNCTIONS
|
|
if no match is found. Allows definitions in an extension file to override the built-in functions.
|
|
*/
|
|
builtin_t lookupbuiltin (char* c) {
|
|
unsigned int start = tablesize(0);
|
|
for (int n=1; n>=0; n--) {
|
|
int entries = tablesize(n);
|
|
for (int i=0; i<entries; i++) {
|
|
if (strcasecmp(c, (char*)(table(n)[i].string)) == 0)
|
|
return (builtin_t)(start + i);
|
|
}
|
|
start = 0;
|
|
}
|
|
return ENDFUNCTIONS;
|
|
}
|
|
|
|
/*
|
|
lookupfn - looks up the entry for name in lookup_table[], and returns the function entry point
|
|
*/
|
|
intptr_t lookupfn (builtin_t name) {
|
|
bool n = name<tablesize(0);
|
|
return (intptr_t)table(n?0:1)[n?name:name-tablesize(0)].fptr;
|
|
}
|
|
|
|
/*
|
|
getminmax - gets the minmax byte from lookup_table[] whose octets specify the type of function
|
|
and minimum and maximum number of arguments for name
|
|
*/
|
|
uint8_t getminmax (builtin_t name) {
|
|
bool n = name<tablesize(0);
|
|
return table(n?0:1)[n?name:name-tablesize(0)].minmax;
|
|
}
|
|
|
|
/*
|
|
checkminmax - checks that the number of arguments nargs for name is within the range specified by minmax
|
|
*/
|
|
void checkminmax (builtin_t name, int nargs) {
|
|
if (!(name < ENDFUNCTIONS)) error2("not a builtin");
|
|
uint8_t minmax = getminmax(name);
|
|
if (nargs<((minmax >> 3) & 0x07)) error2(toofewargs);
|
|
if ((minmax & 0x07) != 0x07 && nargs>(minmax & 0x07)) error2(toomanyargs);
|
|
}
|
|
|
|
/*
|
|
lookupdoc - looks up the documentation string for the built-in function name
|
|
*/
|
|
char *lookupdoc (builtin_t name) {
|
|
bool n = name<tablesize(0);
|
|
return (char*)table(n?0:1)[n?name:name-tablesize(0)].doc;
|
|
}
|
|
|
|
/*
|
|
findsubstring - tests whether a specified substring occurs in the name of a built-in function
|
|
*/
|
|
bool findsubstring (char *part, builtin_t name) {
|
|
bool n = name<tablesize(0);
|
|
return (strstr(table(n?0:1)[n?name:name-tablesize(0)].string, part) != NULL);
|
|
}
|
|
|
|
void testescape () {
|
|
// Context = NIL;
|
|
if (seriell->available() && seriell->read() == '~') { error2(PSTR("escape!")); }
|
|
}
|
|
|
|
/*
|
|
colonp - check that a user-defined symbol starts with a colon and is therefore a keyword
|
|
*/
|
|
bool colonp (symbol_t name) {
|
|
if (!longnamep(name)) return false;
|
|
object *form = (object *)name;
|
|
if (form == NULL) return false;
|
|
return (((form->chars)>>((sizeof(int)-1)*8) & 0xFF) == ':');
|
|
}
|
|
|
|
/*
|
|
keywordp - check that obj is a keyword
|
|
*/
|
|
bool keywordp (object *obj) {
|
|
if (!(symbolp(obj) && builtinp(obj->name))) return false;
|
|
builtin_t name = builtin(obj->name);
|
|
bool n = name<tablesize(0);
|
|
const char *s = table(n?0:1)[n?name:name-tablesize(0)].string;
|
|
char c = s[0];
|
|
return (c == ':');
|
|
}
|
|
|
|
/*
|
|
backtrace - store symbol for backtrace
|
|
*/
|
|
void backtrace (symbol_t name) {
|
|
Backtrace[TraceTop] = (name == sym(NIL)) ? sym(LAMBDA) : name;
|
|
TraceTop = modbacktrace(TraceTop+1);
|
|
if (TraceStart == TraceTop) TraceStart = modbacktrace(TraceStart+1);
|
|
}
|
|
|
|
// Main evaluator
|
|
|
|
#if defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)
|
|
#define ENDSTACK _ebss
|
|
#else
|
|
#define ENDSTACK end
|
|
#endif
|
|
|
|
extern uint32_t ENDSTACK; // Bottom of stack
|
|
|
|
/*
|
|
eval - the main Lisp evaluator
|
|
*/
|
|
object *eval (object *form, object *env) {
|
|
register int *sp asm ("sp");
|
|
int TC=0;
|
|
EVAL:
|
|
// Enough space?
|
|
// Serial.println((uint32_t)sp - (uint32_t)&ENDSTACK); // Find best STACKDIFF value
|
|
// seriell->println((uint32_t)sp - (uint32_t)&ENDSTACK); // Find best STACKDIFF value
|
|
|
|
if (((uint32_t)sp - (uint32_t)&ENDSTACK) < STACKDIFF) { Context = NIL; error2("stack overflow"); }
|
|
if (Freespace <= WORKSPACESIZE>>4) gc(form, env); // GC when 1/16 of workspace left
|
|
// Escape
|
|
if (tstflag(ESCAPE)) { clrflag(ESCAPE); error2("escape!");}
|
|
if (!tstflag(NOESC)) testescape();
|
|
|
|
if (form == NULL) return nil;
|
|
|
|
if (form->type >= NUMBER && form->type <= STRING) return form;
|
|
|
|
if (symbolp(form)) {
|
|
symbol_t name = form->name;
|
|
if (colonp(name)) return form; // Keyword
|
|
object *pair = value(name, env);
|
|
if (pair != NULL) return cdr(pair);
|
|
pair = value(name, GlobalEnv);
|
|
if (pair != NULL) return cdr(pair);
|
|
else if (builtinp(name)) {
|
|
if (name == sym(FEATURES)) return features();
|
|
return form;
|
|
}
|
|
Context = NIL;
|
|
error("undefined", form);
|
|
}
|
|
|
|
#if defined(CODESIZE)
|
|
if (form->type == CODE) error2("can't evaluate CODE header");
|
|
#endif
|
|
|
|
// It's a list
|
|
object *function = car(form);
|
|
object *args = cdr(form);
|
|
|
|
if (function == NULL) error(illegalfn, function);
|
|
if (!listp(args)) error("can't evaluate a dotted pair", args);
|
|
|
|
// List starts with a builtin symbol?
|
|
if (symbolp(function) && builtinp(function->name)) {
|
|
builtin_t name = builtin(function->name);
|
|
|
|
if ((name == LET) || (name == LETSTAR)) {
|
|
if (args == NULL) error2(noargument);
|
|
object *assigns = first(args);
|
|
if (!listp(assigns)) error(notalist, assigns);
|
|
object *forms = cdr(args);
|
|
object *newenv = env;
|
|
protect(newenv);
|
|
while (assigns != NULL) {
|
|
object *assign = car(assigns);
|
|
if (!consp(assign)) push(cons(assign,nil), newenv);
|
|
else if (cdr(assign) == NULL) push(cons(first(assign),nil), newenv);
|
|
else push(cons(first(assign), eval(second(assign),env)), newenv);
|
|
car(GCStack) = newenv;
|
|
if (name == LETSTAR) env = newenv;
|
|
assigns = cdr(assigns);
|
|
}
|
|
env = newenv;
|
|
unprotect();
|
|
form = tf_progn(forms,env);
|
|
goto EVAL;
|
|
}
|
|
|
|
if (name == LAMBDA) {
|
|
if (env == NULL) return form;
|
|
object *envcopy = NULL;
|
|
while (env != NULL) {
|
|
object *pair = first(env);
|
|
if (pair != NULL) push(pair, envcopy);
|
|
env = cdr(env);
|
|
}
|
|
return cons(bsymbol(CLOSURE), cons(envcopy,args));
|
|
}
|
|
|
|
switch(fntype(name)) {
|
|
case SPECIAL_FORMS:
|
|
Context = name;
|
|
checkargs(args);
|
|
return ((fn_ptr_type)lookupfn(name))(args, env);
|
|
|
|
case TAIL_FORMS:
|
|
Context = name;
|
|
checkargs(args);
|
|
form = ((fn_ptr_type)lookupfn(name))(args, env);
|
|
TC = 1;
|
|
goto EVAL;
|
|
|
|
case OTHER_FORMS: error(illegalfn, function);
|
|
}
|
|
}
|
|
|
|
// Evaluate the parameters - result in head
|
|
int TCstart = TC;
|
|
object *head;
|
|
if (consp(function) && !(isbuiltin(car(function), LAMBDA) || isbuiltin(car(function), CLOSURE)
|
|
|| car(function)->type == CODE)) { Context = NIL; error(illegalfn, function); }
|
|
if (symbolp(function)) {
|
|
object *pair = findpair(function, env);
|
|
if (pair != NULL) head = cons(cdr(pair), NULL); else head = cons(function, NULL);
|
|
} else head = cons(eval(function, env), NULL);
|
|
protect(head); // Don't GC the result list
|
|
object *tail = head;
|
|
form = cdr(form);
|
|
int nargs = 0;
|
|
|
|
while (form != NULL){
|
|
object *obj = cons(eval(car(form),env),NULL);
|
|
cdr(tail) = obj;
|
|
tail = obj;
|
|
form = cdr(form);
|
|
nargs++;
|
|
}
|
|
|
|
object *fname = function;
|
|
function = car(head);
|
|
args = cdr(head);
|
|
|
|
if (symbolp(function)) {
|
|
if (!builtinp(function->name)) { Context = NIL; error(illegalfn, function); }
|
|
builtin_t bname = builtin(function->name);
|
|
Context = bname;
|
|
checkminmax(bname, nargs);
|
|
object *result = ((fn_ptr_type)lookupfn(bname))(args, env);
|
|
unprotect();
|
|
return result;
|
|
}
|
|
|
|
if (consp(function)) {
|
|
symbol_t name = sym(NIL);
|
|
if (!listp(fname)) name = fname->name;
|
|
|
|
if (isbuiltin(car(function), LAMBDA)) {
|
|
if (tstflag(BACKTRACE)) backtrace(name);
|
|
form = closure(TCstart, name, function, args, &env);
|
|
unprotect();
|
|
int trace = tracing(name);
|
|
if (trace || tstflag(BACKTRACE)) {
|
|
object *result = eval(form, env);
|
|
if (trace) {
|
|
indent((--(TraceDepth[trace-1]))<<1, ' ', pserial);
|
|
pint(TraceDepth[trace-1], pserial);
|
|
pserial(':'); pserial(' ');
|
|
printobject(fname, pserial); pfstring(" returned ", pserial);
|
|
printobject(result, pserial); pln(pserial);
|
|
}
|
|
if (tstflag(BACKTRACE)) TraceTop = modbacktrace(TraceTop-1);
|
|
return result;
|
|
} else {
|
|
TC = 1;
|
|
goto EVAL;
|
|
}
|
|
}
|
|
|
|
if (isbuiltin(car(function), CLOSURE)) {
|
|
function = cdr(function);
|
|
if (tstflag(BACKTRACE)) backtrace(name);
|
|
form = closure(TCstart, name, function, args, &env);
|
|
unprotect();
|
|
if (tstflag(BACKTRACE)) {
|
|
object *result = eval(form, env);
|
|
TraceTop = modbacktrace(TraceTop-1);
|
|
return result;
|
|
} else {
|
|
TC = 1;
|
|
goto EVAL;
|
|
}
|
|
}
|
|
|
|
if (car(function)->type == CODE) {
|
|
int n = listlength(second(function));
|
|
if (nargs<n) errorsym2(fname->name, toofewargs);
|
|
if (nargs>n) errorsym2(fname->name, toomanyargs);
|
|
uint32_t entry = startblock(car(function)) + 1;
|
|
unprotect();
|
|
return call(entry, n, args, env);
|
|
}
|
|
|
|
}
|
|
error(illegalfn, fname); return nil;
|
|
}
|
|
|
|
// Print functions
|
|
|
|
/*
|
|
pserial - prints a character to the serial port
|
|
*/
|
|
void pserial (char c) {
|
|
LastPrint = c;
|
|
if (!tstflag(NOECHO)) Display(c); // Don't display on T-Deck when paste in listing
|
|
if (c == '\n') seriell->write('\r');
|
|
seriell->write(c);
|
|
}
|
|
|
|
const char ControlCodes[] = "Null\0SOH\0STX\0ETX\0EOT\0ENQ\0ACK\0Bell\0Backspace\0Tab\0Newline\0VT\0"
|
|
"Page\0Return\0SO\0SI\0DLE\0DC1\0DC2\0DC3\0DC4\0NAK\0SYN\0ETB\0CAN\0EM\0SUB\0Escape\0FS\0GS\0RS\0US\0Space\0";
|
|
|
|
/*
|
|
pcharacter - prints a character to a stream, escaping special characters if PRINTREADABLY is false
|
|
If <= 32 prints character name; eg #\Space
|
|
If < 127 prints ASCII; eg #\A
|
|
Otherwise prints decimal; eg #\234
|
|
*/
|
|
void pcharacter (uint8_t c, pfun_t pfun) {
|
|
if (!tstflag(PRINTREADABLY)) pfun(c);
|
|
else {
|
|
pfun('#'); pfun('\\');
|
|
if (c <= 32) {
|
|
const char *p = ControlCodes;
|
|
while (c > 0) {p = p + strlen(p) + 1; c--; }
|
|
pfstring(p, pfun);
|
|
} else if (c < 127) pfun(c);
|
|
else pint(c, pfun);
|
|
}
|
|
}
|
|
|
|
/*
|
|
pstring - prints a C string to the specified stream
|
|
*/
|
|
void pstring (char *s, pfun_t pfun) {
|
|
while (*s) pfun(*s++);
|
|
}
|
|
|
|
/*
|
|
plispstring - prints a Lisp string object to the specified stream
|
|
*/
|
|
void plispstring (object *form, pfun_t pfun) {
|
|
plispstr(form->name, pfun);
|
|
}
|
|
|
|
/*
|
|
plispstr - prints a Lisp string name to the specified stream
|
|
*/
|
|
void plispstr (symbol_t name, pfun_t pfun) {
|
|
object *form = (object *)name;
|
|
while (form != NULL) {
|
|
int chars = form->chars;
|
|
for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) {
|
|
char ch = chars>>i & 0xFF;
|
|
if (tstflag(PRINTREADABLY) && (ch == '"' || ch == '\\')) pfun('\\');
|
|
if (ch) pfun(ch);
|
|
}
|
|
form = car(form);
|
|
}
|
|
}
|
|
|
|
/*
|
|
printstring - prints a Lisp string object to the specified stream
|
|
taking account of the PRINTREADABLY flag
|
|
*/
|
|
void printstring (object *form, pfun_t pfun) {
|
|
if (tstflag(PRINTREADABLY)) pfun('"');
|
|
plispstr(form->name, pfun);
|
|
if (tstflag(PRINTREADABLY)) pfun('"');
|
|
}
|
|
|
|
/*
|
|
pbuiltin - prints a built-in symbol to the specified stream
|
|
*/
|
|
void pbuiltin (builtin_t name, pfun_t pfun) {
|
|
int n = name<tablesize(0);
|
|
const char *s = table(n?0:1)[n?name:name-tablesize(0)].string;
|
|
while (1) {
|
|
char c = *s++;
|
|
if (c == 0) return;
|
|
pfun(c);
|
|
}
|
|
}
|
|
|
|
/*
|
|
pradix40 - prints a radix 40 symbol to the specified stream
|
|
*/
|
|
void pradix40 (symbol_t name, pfun_t pfun) {
|
|
uint32_t x = untwist(name);
|
|
for (int d=102400000; d>0; d = d/40) {
|
|
uint32_t j = x/d;
|
|
char c = fromradix40(j);
|
|
if (c == 0) return;
|
|
pfun(c); x = x - j*d;
|
|
}
|
|
}
|
|
|
|
/*
|
|
printsymbol - prints any symbol from a symbol object to the specified stream
|
|
*/
|
|
void printsymbol (object *form, pfun_t pfun) {
|
|
psymbol(form->name, pfun);
|
|
}
|
|
|
|
/*
|
|
psymbol - prints any symbol from a symbol name to the specified stream
|
|
*/
|
|
void psymbol (symbol_t name, pfun_t pfun) {
|
|
if (longnamep(name)) plispstr(name, pfun);
|
|
else {
|
|
uint32_t value = untwist(name);
|
|
if (value < PACKEDS) error2("invalid symbol");
|
|
else if (value >= BUILTINS) pbuiltin((builtin_t)(value-BUILTINS), pfun);
|
|
else pradix40(name, pfun);
|
|
}
|
|
}
|
|
|
|
/*
|
|
pfstring - prints a string from flash memory to the specified stream
|
|
*/
|
|
void pfstring (const char *s, pfun_t pfun) {
|
|
while (1) {
|
|
char c = *s++;
|
|
if (c == 0) return;
|
|
pfun(c);
|
|
}
|
|
}
|
|
|
|
/*
|
|
pint - prints an integer in decimal to the specified stream
|
|
*/
|
|
void pint (int i, pfun_t pfun) {
|
|
uint32_t j = i;
|
|
if (i<0) { pfun('-'); j=-i; }
|
|
pintbase(j, 10, pfun);
|
|
}
|
|
|
|
/*
|
|
pintbase - prints an integer in base 'base' to the specified stream
|
|
*/
|
|
void pintbase (uint32_t i, uint8_t base, pfun_t pfun) {
|
|
int lead = 0; uint32_t p = 1000000000;
|
|
if (base == 2) p = 0x80000000; else if (base == 16) p = 0x10000000;
|
|
for (uint32_t d=p; d>0; d=d/base) {
|
|
uint32_t j = i/d;
|
|
if (j!=0 || lead || d==1) { pfun((j<10) ? j+'0' : j+'W'); lead=1;}
|
|
i = i - j*d;
|
|
}
|
|
}
|
|
|
|
/*
|
|
pinthex4 - prints a four-digit hexadecimal number with leading zeros to the specified stream
|
|
*/
|
|
void printhex4 (int i, pfun_t pfun) {
|
|
int p = 0x1000;
|
|
for (int d=p; d>0; d=d/16) {
|
|
int j = i/d;
|
|
pfun((j<10) ? j+'0' : j + 'W');
|
|
i = i - j*d;
|
|
}
|
|
pfun(' ');
|
|
}
|
|
|
|
/*
|
|
pmantissa - prints the mantissa of a floating-point number to the specified stream
|
|
*/
|
|
void pmantissa (float f, pfun_t pfun) {
|
|
int sig = floor(log10(f));
|
|
int mul = pow(10, 5 - sig);
|
|
int i = round(f * mul);
|
|
bool point = false;
|
|
if (i == 1000000) { i = 100000; sig++; }
|
|
if (sig < 0) {
|
|
pfun('0'); pfun('.'); point = true;
|
|
for (int j=0; j < - sig - 1; j++) pfun('0');
|
|
}
|
|
mul = 100000;
|
|
for (int j=0; j<7; j++) {
|
|
int d = (int)(i / mul);
|
|
pfun(d + '0');
|
|
i = i - d * mul;
|
|
if (i == 0) {
|
|
if (!point) {
|
|
for (int k=j; k<sig; k++) pfun('0');
|
|
pfun('.'); pfun('0');
|
|
}
|
|
return;
|
|
}
|
|
if (j == sig && sig >= 0) { pfun('.'); point = true; }
|
|
mul = mul / 10;
|
|
}
|
|
}
|
|
|
|
/*
|
|
pfloat - prints a floating-point number to the specified stream
|
|
*/
|
|
void pfloat (float f, pfun_t pfun) {
|
|
if (isnan(f)) { pfstring("NaN", pfun); return; }
|
|
if (f == 0.0) { pfun('0'); return; }
|
|
if (isinf(f)) { pfstring("Inf", pfun); return; }
|
|
if (f < 0) { pfun('-'); f = -f; }
|
|
// Calculate exponent
|
|
int e = 0;
|
|
if (f < 1e-3 || f >= 1e5) {
|
|
e = floor(log(f) / 2.302585); // log10 gives wrong result
|
|
f = f / pow(10, e);
|
|
}
|
|
|
|
pmantissa (f, pfun);
|
|
|
|
// Exponent
|
|
if (e != 0) {
|
|
pfun('e');
|
|
pint(e, pfun);
|
|
}
|
|
}
|
|
|
|
/*
|
|
pln - prints a newline to the specified stream
|
|
*/
|
|
inline void pln (pfun_t pfun) {
|
|
pfun('\n');
|
|
}
|
|
|
|
/*
|
|
pfl - prints a newline to the specified stream if a newline has not just been printed
|
|
*/
|
|
void pfl (pfun_t pfun) {
|
|
if (LastPrint != '\n') pfun('\n');
|
|
}
|
|
|
|
/*
|
|
plist - prints a list to the specified stream
|
|
*/
|
|
void plist (object *form, pfun_t pfun) {
|
|
pfun('(');
|
|
printobject(car(form), pfun);
|
|
form = cdr(form);
|
|
while (form != NULL && listp(form)) {
|
|
pfun(' ');
|
|
printobject(car(form), pfun);
|
|
form = cdr(form);
|
|
}
|
|
if (form != NULL) {
|
|
pfstring(" . ", pfun);
|
|
printobject(form, pfun);
|
|
}
|
|
pfun(')');
|
|
}
|
|
|
|
/*
|
|
pstream - prints a stream name to the specified stream
|
|
*/
|
|
void pstream (object *form, pfun_t pfun) {
|
|
pfun('<');
|
|
pfstring(streamname[(form->integer)>>8], pfun);
|
|
pfstring("-stream ", pfun);
|
|
pint(form->integer & 0xFF, pfun);
|
|
pfun('>');
|
|
}
|
|
|
|
/*
|
|
printobject - prints any Lisp object to the specified stream
|
|
*/
|
|
void printobject (object *form, pfun_t pfun) {
|
|
if (form == NULL) pfstring("nil", pfun);
|
|
else if (listp(form) && isbuiltin(car(form), CLOSURE)) pfstring("<closure>", pfun);
|
|
else if (listp(form)) plist(form, pfun);
|
|
else if (integerp(form)) pint(form->integer, pfun);
|
|
else if (floatp(form)) pfloat(form->single_float, pfun);
|
|
else if (symbolp(form)) { if (form->name != sym(NOTHING)) printsymbol(form, pfun); }
|
|
else if (characterp(form)) pcharacter(form->chars, pfun);
|
|
else if (stringp(form)) printstring(form, pfun);
|
|
else if (arrayp(form)) printarray(form, pfun);
|
|
else if (form->type == CODE) pfstring("code", pfun);
|
|
else if (streamp(form)) pstream(form, pfun);
|
|
else error2("error in print");
|
|
}
|
|
|
|
/*
|
|
prin1object - prints any Lisp object to the specified stream escaping special characters
|
|
*/
|
|
void prin1object (object *form, pfun_t pfun) {
|
|
flags_t temp = Flags;
|
|
clrflag(PRINTREADABLY);
|
|
printobject(form, pfun);
|
|
Flags = temp;
|
|
}
|
|
|
|
// Read functions
|
|
|
|
/*
|
|
glibrary - reads a character from the Lisp Library
|
|
*/
|
|
int
|
|
glibrary()
|
|
{
|
|
if (LastChar) {
|
|
char temp = LastChar;
|
|
LastChar = 0;
|
|
|
|
return temp;
|
|
}
|
|
|
|
char c = LispLibrary[GlobalStringIndex++];
|
|
return (c != 0) ? c : -1; // -1?
|
|
}
|
|
|
|
/*
|
|
loadfromlibrary - reads and evaluates a form from the Lisp Library
|
|
*/
|
|
void
|
|
loadfromlibrary(object *env)
|
|
{
|
|
GlobalStringIndex = 0;
|
|
object *line = read(glibrary);
|
|
while (line != NULL) {
|
|
protect(line);
|
|
eval(line, env);
|
|
unprotect();
|
|
line = read(glibrary);
|
|
}
|
|
}
|
|
|
|
// PicoCalc terminal and keyboard support
|
|
const int Columns = 53;
|
|
const int Leading = 10; // Between 8 and 10
|
|
const int Lines = 320/Leading;
|
|
const int LastColumn = Columns-1;
|
|
const int LastLine = Lines-1;
|
|
const char Cursor = 0x5f;
|
|
|
|
uint8_t Scroll = 0;
|
|
|
|
// For line editor
|
|
const int TerminalWidth = 80;
|
|
volatile int WritePtr = 0, ReadPtr = 0, LastWritePtr = 0;
|
|
const int KybdBufSize = Columns*Lines;
|
|
char KybdBuf[KybdBufSize], ScrollBuf[Columns][Lines];
|
|
volatile uint8_t KybdAvailable = 0;
|
|
|
|
// Parenthesis highlighting
|
|
void esc (int p, char c) {
|
|
seriell->write('\e'); seriell->write('[');
|
|
seriell->write((char)('0'+ p/100));
|
|
seriell->write((char)('0'+ (p/10) % 10));
|
|
seriell->write((char)('0'+ p % 10));
|
|
seriell->write(c);
|
|
}
|
|
|
|
void hilight (char c) {
|
|
seriell->write('\e'); seriell->write('['); seriell->write(c); seriell->write('m');
|
|
}
|
|
|
|
/*
|
|
Highlight - handles parenthesis highlighting with the line editor
|
|
*/
|
|
void Highlight (int p, int wp, uint8_t invert) {
|
|
wp = wp + 2; // Prompt
|
|
#if defined (printfreespace)
|
|
int f = Freespace;
|
|
while (f) { wp++; f=f/10; }
|
|
#endif
|
|
int line = wp/TerminalWidth;
|
|
int col = wp%TerminalWidth;
|
|
int targetline = (wp - p)/TerminalWidth;
|
|
int targetcol = (wp - p)%TerminalWidth;
|
|
int up = line-targetline, left = col-targetcol;
|
|
if (p) {
|
|
if (up) esc(up, 'A');
|
|
if (col > targetcol) esc(left, 'D'); else esc(-left, 'C');
|
|
if (invert) hilight('7');
|
|
seriell->write('('); seriell->write('\b');
|
|
// Go back
|
|
if (up) esc(up, 'B'); // Down
|
|
if (col > targetcol) esc(left, 'C'); else esc(-left, 'D');
|
|
seriell->write('\b'); seriell->write(')');
|
|
if (invert) hilight('0');
|
|
}
|
|
}
|
|
|
|
/*
|
|
processkey - handles keys in the line editor
|
|
*/
|
|
void processkey (char c) {
|
|
if (c == 27) { setflag(ESCAPE); return; } // Escape key
|
|
#if defined(vt100)
|
|
static int parenthesis = 0, wp = 0;
|
|
// Undo previous parenthesis highlight
|
|
Highlight(parenthesis, wp, 0);
|
|
parenthesis = 0;
|
|
#endif
|
|
// Edit buffer
|
|
if (c == '\n' || c == '\r') {
|
|
pserial('\n');
|
|
KybdAvailable = 1;
|
|
ReadPtr = 0; LastWritePtr = WritePtr;
|
|
return;
|
|
}
|
|
if (c == 8 || c == 0x7f) { // Backspace key
|
|
if (WritePtr > 0) {
|
|
WritePtr--;
|
|
seriell->write(8); seriell->write(' '); seriell->write(8);
|
|
if (WritePtr) c = KybdBuf[WritePtr-1];
|
|
}
|
|
} else if (c == 9) { // tab or ctrl-I
|
|
for (int i = 0; i < LastWritePtr; i++) Serial.write(KybdBuf[i]);
|
|
WritePtr = LastWritePtr;
|
|
} else if (WritePtr < KybdBufSize) {
|
|
KybdBuf[WritePtr++] = c;
|
|
seriell->write(c);
|
|
}
|
|
#if defined(vt100)
|
|
// Do new parenthesis highlight
|
|
if (c == ')') {
|
|
int search = WritePtr-1, level = 0;
|
|
while (search >= 0 && parenthesis == 0) {
|
|
c = KybdBuf[search--];
|
|
if (c == ')') level++;
|
|
if (c == '(') {
|
|
level--;
|
|
if (level == 0) {parenthesis = WritePtr-search-1; wp = WritePtr; }
|
|
}
|
|
}
|
|
Highlight(parenthesis, wp, 1);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/*
|
|
gserial - gets a character from the serial port
|
|
*/
|
|
int gserial () {
|
|
if (LastChar) {
|
|
char temp = LastChar;
|
|
LastChar = 0;
|
|
return temp;
|
|
}
|
|
#if defined(lineeditor)
|
|
while (!KybdAvailable) {
|
|
if (seriell->available()) {
|
|
char temp = seriell->read();
|
|
processkey(temp);
|
|
}
|
|
#if defined(i2ckbd)
|
|
if (pc_kbd.keyCount() > 0) {
|
|
const PCKeyboard::KeyEvent key = pc_kbd.keyEvent();
|
|
if (key.state == PCKeyboard::StatePress) {
|
|
char temp = key.key;
|
|
if ((temp != 0) && (temp !=255) && (temp != 0xA1) && (temp != 0xA2) && (temp != 0xA3) && (temp != 0xA4) && (temp != 0xA5)) {
|
|
ProcessKey(temp);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (ReadPtr != WritePtr) return KybdBuf[ReadPtr++];
|
|
KybdAvailable = 0;
|
|
WritePtr = 0;
|
|
return '\n';
|
|
#else
|
|
unsigned long start = millis();
|
|
while (!seriell->available()) if (millis() - start > 1000) clrflag(NOECHO);
|
|
char temp = seriell->read();
|
|
if (temp != '\n' && !tstflag(NOECHO)) pserial(temp);
|
|
return temp;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
nextitem - reads the next token from the specified stream
|
|
*/
|
|
object *nextitem (gfun_t gfun) {
|
|
int ch = gfun();
|
|
while(issp(ch)) {
|
|
ch = gfun();
|
|
}
|
|
|
|
if (ch == ';') {
|
|
do { ch = gfun(); if (ch == ';' || ch == '(') setflag(NOECHO); }
|
|
while(ch != '(');
|
|
}
|
|
if (ch == '\n') ch = gfun();
|
|
if (ch == -1) return nil;
|
|
if (ch == ')') return (object *)KET;
|
|
if (ch == '(') return (object *)BRA;
|
|
if (ch == '\'') return (object *)QUO;
|
|
|
|
// Parse string
|
|
if (ch == '"') return readstring('"', true, gfun);
|
|
|
|
// Parse symbol, character, or number
|
|
int index = 0, base = 10, sign = 1;
|
|
char buffer[BUFFERSIZE];
|
|
int bufmax = BUFFERSIZE-3; // Max index
|
|
unsigned int result = 0;
|
|
bool isfloat = false;
|
|
float fresult = 0.0;
|
|
|
|
if (ch == '+') {
|
|
buffer[index++] = ch;
|
|
ch = gfun();
|
|
} else if (ch == '-') {
|
|
sign = -1;
|
|
buffer[index++] = ch;
|
|
ch = gfun();
|
|
} else if (ch == '.') {
|
|
buffer[index++] = ch;
|
|
ch = gfun();
|
|
if (ch == ' ') return (object *)DOT;
|
|
isfloat = true;
|
|
}
|
|
|
|
// Parse reader macros
|
|
else if (ch == '#') {
|
|
ch = gfun();
|
|
char ch2 = ch & ~0x20; // force to upper case
|
|
if (ch == '\\') { // Character
|
|
base = 0; ch = gfun();
|
|
if (issp(ch) || isbr(ch)) return character(ch);
|
|
else LastChar = ch;
|
|
} else if (ch == '|') {
|
|
do { while (gfun() != '|'); }
|
|
while (gfun() != '#');
|
|
return nextitem(gfun);
|
|
} else if (ch2 == 'B') base = 2;
|
|
else if (ch2 == 'O') base = 8;
|
|
else if (ch2 == 'X') base = 16;
|
|
else if (ch == '\'') return nextitem(gfun);
|
|
else if (ch == '.') {
|
|
setflag(NOESC);
|
|
object *result = eval(read(gfun), NULL);
|
|
clrflag(NOESC);
|
|
return result;
|
|
}
|
|
else if (ch == '(') { LastChar = ch; return readarray(1, read(gfun)); }
|
|
else if (ch == '*') return readbitarray(gfun);
|
|
else if (ch >= '1' && ch <= '9' && (gfun() & ~0x20) == 'A') return readarray(ch - '0', read(gfun));
|
|
else error2("illegal character after #");
|
|
ch = gfun();
|
|
}
|
|
int valid; // 0=undecided, -1=invalid, +1=valid
|
|
if (ch == '.') valid = 0; else if (digitvalue(ch)<base) valid = 1; else valid = -1;
|
|
bool isexponent = false;
|
|
int exponent = 0, esign = 1;
|
|
float divisor = 10.0;
|
|
|
|
while(!issp(ch) && !isbr(ch) && index < bufmax) {
|
|
buffer[index++] = ch;
|
|
if (base == 10 && ch == '.' && !isexponent) {
|
|
isfloat = true;
|
|
fresult = result;
|
|
} else if (base == 10 && (ch == 'e' || ch == 'E')) {
|
|
if (!isfloat) { isfloat = true; fresult = result; }
|
|
isexponent = true;
|
|
if (valid == 1) valid = 0; else valid = -1;
|
|
} else if (isexponent && ch == '-') {
|
|
esign = -esign;
|
|
} else if (isexponent && ch == '+') {
|
|
} else {
|
|
int digit = digitvalue(ch);
|
|
if (digitvalue(ch)<base && valid != -1) valid = 1; else valid = -1;
|
|
if (isexponent) {
|
|
exponent = exponent * 10 + digit;
|
|
} else if (isfloat) {
|
|
fresult = fresult + digit / divisor;
|
|
divisor = divisor * 10.0;
|
|
} else {
|
|
result = result * base + digit;
|
|
}
|
|
}
|
|
ch = gfun();
|
|
}
|
|
|
|
buffer[index] = '\0';
|
|
if (isbr(ch)) LastChar = ch;
|
|
if (isfloat && valid == 1) return makefloat(fresult * sign * pow(10, exponent * esign));
|
|
else if (valid == 1) {
|
|
if (base == 10 && result > ((unsigned int)INT_MAX+(1-sign)/2))
|
|
return makefloat((float)result*sign);
|
|
return number(result*sign);
|
|
} else if (base == 0) {
|
|
if (index == 1) return character(buffer[0]);
|
|
const char *p = ControlCodes; char c = 0;
|
|
while (c < 33) {
|
|
if (strcasecmp(buffer, p) == 0) return character(c);
|
|
p = p + strlen(p) + 1; c++;
|
|
}
|
|
if (index == 3) return character((buffer[0]*10+buffer[1])*10+buffer[2]-5328);
|
|
error2("unknown character");
|
|
}
|
|
|
|
builtin_t x = lookupbuiltin(buffer);
|
|
if (x == NIL) return nil;
|
|
if (x != ENDFUNCTIONS) return bsymbol(x);
|
|
if (index <= 6 && valid40(buffer)) return intern(twist(pack40(buffer)));
|
|
return internlong(buffer);
|
|
}
|
|
|
|
/*
|
|
readrest - reads the remaining tokens from the specified stream
|
|
*/
|
|
object *readrest (gfun_t gfun) {
|
|
object *item = nextitem(gfun);
|
|
object *head = NULL;
|
|
object *tail = NULL;
|
|
|
|
while (item != (object *)KET) {
|
|
if (item == (object *)BRA) {
|
|
item = readrest(gfun);
|
|
} else if (item == (object *)QUO) {
|
|
item = cons(bsymbol(QUOTE), cons(read(gfun), NULL));
|
|
} else if (item == (object *)DOT) {
|
|
tail->cdr = read(gfun);
|
|
if (readrest(gfun) != NULL) error2("malformed list");
|
|
return head;
|
|
} else {
|
|
object *cell = cons(item, NULL);
|
|
if (head == NULL) head = cell;
|
|
else tail->cdr = cell;
|
|
tail = cell;
|
|
item = nextitem(gfun);
|
|
}
|
|
}
|
|
return head;
|
|
}
|
|
|
|
/*
|
|
read - recursively reads a Lisp object from the stream gfun and returns it
|
|
*/
|
|
object *read (gfun_t gfun) {
|
|
object *item = nextitem(gfun);
|
|
if (item == (object *)KET) error2("incomplete list");
|
|
if (item == (object *)BRA) {
|
|
return readrest(gfun);
|
|
}
|
|
if (item == (object *)DOT) {
|
|
return read(gfun);
|
|
}
|
|
if (item == (object *)QUO) {
|
|
return cons(bsymbol(QUOTE), cons(read(gfun), NULL));
|
|
}
|
|
return item;
|
|
}
|
|
|
|
// Terminal **********************************************************************************
|
|
|
|
// Plot character at absolute character cell position
|
|
void PlotChar (uint8_t ch, uint8_t line, uint8_t column) {
|
|
#if defined(gfxsupport)
|
|
uint16_t y = line*Leading;
|
|
uint16_t x = column*6;
|
|
ScrollBuf[column][(line+Scroll) % Lines] = ch;
|
|
if (ch & 0x80) {
|
|
tft.drawChar(x, y, ch & 0x7f, BG_COLOR, TEXT_COLOR, 1);
|
|
} else {
|
|
tft.drawChar(x, y, ch & 0x7f, TEXT_COLOR, BG_COLOR, 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Clears the bottom line and then scrolls the display up by one line
|
|
void ScrollDisplay () {
|
|
#if defined(gfxsupport)
|
|
tft.fillRect(0, 320-Leading, 320, 10, TFT_BLACK);
|
|
for (uint8_t x = 0; x < Columns; x++) {
|
|
char c = ScrollBuf[x][Scroll];
|
|
for (uint8_t y = 0; y < Lines-1; y++) {
|
|
char c2 = ScrollBuf[x][(y+Scroll+1) % Lines];
|
|
if (c != c2) {
|
|
if (c2 & 0x80) {
|
|
tft.drawChar(x*6, y*Leading, c2 & 0x7f, BG_COLOR, TFT_GREEN, 1);
|
|
} else {
|
|
tft.drawChar(x*6, y*Leading, c2 & 0x7f, TEXT_COLOR, BG_COLOR, 1);
|
|
}
|
|
c = c2;
|
|
}
|
|
}
|
|
}
|
|
// Tidy up graphics
|
|
for (uint8_t y = 0; y < Lines-1; y++) tft.fillRect(0, y*Leading+8, 320, 2, TFT_BLACK);
|
|
tft.fillRect(318, 0, 3, 320, TFT_BLACK);
|
|
for (int x=0; x<Columns; x++) ScrollBuf[x][Scroll] = 0;
|
|
Scroll = (Scroll + 1) % Lines;
|
|
#endif
|
|
}
|
|
|
|
const char VT = 11; // Vertical tab
|
|
const char BEEP = 7;
|
|
const char STX = 2;
|
|
const char ETX = 3;
|
|
|
|
// Prints a character to display, with cursor, handling control characters
|
|
void Display (char c) {
|
|
#if defined(gfxsupport)
|
|
static uint8_t line = 0, column = 0;
|
|
static bool invert = false;
|
|
// These characters don't affect the cursor
|
|
if (c == 8) { // Backspace
|
|
//PlotChar(' ', line+Scroll, Column); //hide cursor?
|
|
if (column == 0) {
|
|
line--; column = LastColumn;
|
|
} else column--;
|
|
//PlotChar(Cursor, line+Scroll, Column); //show cursor
|
|
return;
|
|
}
|
|
if (c == 9) { // Cursor forward
|
|
if (column == LastColumn) {
|
|
line++; column = 0;
|
|
} else column++;
|
|
return;
|
|
}
|
|
if ((c >= 17) && (c <= 20)) { // Parentheses
|
|
if (c == 17) PlotChar('(', line, column);
|
|
else if (c == 18) PlotChar('(' | 0x80, line, column);
|
|
else if (c == 19) PlotChar(')', line, column);
|
|
else PlotChar(')' | 0x80, line, column);
|
|
return;
|
|
}
|
|
if (c == STX) { invert = true; return; }
|
|
if (c == ETX) { invert = false; return; }
|
|
// Hide cursor
|
|
PlotChar(' ', line, column);
|
|
if (c == 0x7F) { // DEL
|
|
if (column == 0) {
|
|
line--; column = LastColumn;
|
|
} else column--;
|
|
} else if ((c & 0x7f) >= 32) { // Normal character
|
|
if (invert) PlotChar(c | 0x80, line, column++); else PlotChar(c, line, column++);
|
|
if (column > LastColumn) {
|
|
column = 0;
|
|
if (line == LastLine) ScrollDisplay(); else line++;
|
|
}
|
|
// Control characters
|
|
} else if (c == 12) { // Clear display
|
|
tft.fillScreen(COLOR_BLACK); line = 0; column = 0; Scroll = 0;
|
|
for (int col = 0; col < Columns; col++) {
|
|
for (int row = 0; row < Lines; row++) {
|
|
ScrollBuf[col][row] = 0;
|
|
}
|
|
}
|
|
} else if (c == '\n') { // Newline
|
|
column = 0;
|
|
if (line == LastLine) ScrollDisplay(); else line++;
|
|
} else if (c == VT) {
|
|
column = 0; Scroll = 0; line = LastLine - 2;
|
|
} else if (c == BEEP) tone(0, 440, 125); // Beep
|
|
// Show cursor
|
|
PlotChar(Cursor, line, column);
|
|
#endif
|
|
}
|
|
|
|
// Keyboard **********************************************************************************
|
|
|
|
void initkybd () {
|
|
#ifdef ULISP_I2C1
|
|
Wire1.setSDA(6);
|
|
Wire1.setSCL(7);
|
|
Wire1.begin();
|
|
Wire1.setClock(10000);
|
|
pc_kbd.begin(0x1f,&Wire1);
|
|
#else
|
|
Wire.setSDA(4);
|
|
Wire.setSCL(5);
|
|
Wire.begin();
|
|
Wire.setClock(10000);
|
|
pc_kbd.begin();
|
|
#endif
|
|
}
|
|
|
|
|
|
// Parenthesis highlighting
|
|
void HighLight(int p, uint8_t invert) {
|
|
if (p) {
|
|
for (int n=0; n < p; n++) Display(8);
|
|
Display(17 + invert);
|
|
for (int n=1; n < p; n++) Display(9);
|
|
Display(19 + invert);
|
|
Display(9);
|
|
}
|
|
}
|
|
void ProcessKey (char c) {
|
|
static int parenthesis = 0;
|
|
if (c == 27) { setflag(ESCAPE); return; } // Escape key
|
|
// Undo previous parenthesis highlight
|
|
HighLight(parenthesis, 0);
|
|
parenthesis = 0;
|
|
// Edit buffer
|
|
if (c == '\n' || c == '\r') {
|
|
pserial('\n');
|
|
KybdAvailable = 1;
|
|
ReadPtr = 0;
|
|
return;
|
|
}
|
|
if (c == 8 || c == 0x7f) { // Backspace key
|
|
if (WritePtr > 0) {
|
|
WritePtr--;
|
|
Display(0x7F);
|
|
if (WritePtr) c = KybdBuf[WritePtr-1];
|
|
}
|
|
} else if (WritePtr < KybdBufSize) {
|
|
KybdBuf[WritePtr++] = c;
|
|
Display(c);
|
|
}
|
|
// Do new parenthesis highlight
|
|
if (c == ')') {
|
|
int search = WritePtr-1, level = 0;
|
|
while (search >= 0 && parenthesis == 0) {
|
|
c = KybdBuf[search--];
|
|
if (c == ')') level++;
|
|
if (c == '(') {
|
|
level--;
|
|
if (level == 0) parenthesis = WritePtr-search-1;
|
|
}
|
|
}
|
|
HighLight(parenthesis, 1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
// Setup
|
|
|
|
/*
|
|
initenv - initialises the uLisp environment
|
|
*/
|
|
void initenv () {
|
|
GlobalEnv = NULL;
|
|
tee = bsymbol(TEE);
|
|
}
|
|
|
|
/*
|
|
initgfx - initialises the graphics
|
|
*/
|
|
void
|
|
initgfx()
|
|
{
|
|
#if defined(gfxsupport)
|
|
#if defined(ARDUINO_PYBADGE_M4) || defined(ARDUINO_PYGAMER_M4)
|
|
tft.initR(INITR_BLACKTAB);
|
|
tft.setRotation(1);
|
|
pinMode(TFT_BACKLIGHT, OUTPUT);
|
|
digitalWrite(TFT_BACKLIGHT, HIGH);
|
|
tft.fillScreen(0);
|
|
#elif defined(ARDUINO_WIO_TERMINAL)
|
|
tft.init();
|
|
tft.setRotation(3);
|
|
tft.fillScreen(TFT_BLACK);
|
|
#elif defined(ARDUINO_NRF52840_CLUE)
|
|
tft.init(240, 240);
|
|
tft.setRotation(1);
|
|
tft.fillScreen(0);
|
|
pinMode(34, OUTPUT); // Backlight
|
|
digitalWrite(34, HIGH);
|
|
#elif defined(RASPBERRY_PI_PICO_PLATFORM)
|
|
tft.init();
|
|
#if defined(PLATFORM_PICOCALC)
|
|
tft.setRotation(0);
|
|
tft.invertDisplay(1);
|
|
#endif
|
|
tft.fillScreen(TFT_BLACK);
|
|
tft.setTextColor(TEXT_COLOR);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void setup () {
|
|
Serial.begin(9600);
|
|
#ifdef ULISP_SERIAL1
|
|
seriell = &Serial1;
|
|
#else
|
|
seriell = &Serial;
|
|
#endif
|
|
|
|
seriell->begin(BAUDRATE*100);
|
|
|
|
int start = millis();
|
|
|
|
while ((millis() - start) < 5000) {
|
|
#ifdef ULISP_SERIAL1
|
|
if (Serial1) break;
|
|
#else
|
|
if (Serial) break;
|
|
#endif
|
|
}
|
|
|
|
#if defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_2W)
|
|
#if defined(sdcardsupport) && defined(PLATFORM_PICOCALC)
|
|
// picocalc doesn't have touch screen
|
|
pinMode(SDCARD_SS_PIN,OUTPUT);
|
|
digitalWrite(SDCARD_SS_PIN,1);
|
|
#endif
|
|
#endif
|
|
|
|
|
|
initworkspace();
|
|
initenv();
|
|
initsleep();
|
|
initgfx();
|
|
#if defined(i2ckbd)
|
|
initkybd();
|
|
#endif
|
|
pfstring(PSTR("uLisp 4.7b "), pserial); pln(pserial);
|
|
}
|
|
|
|
// Read/Evaluate/Print loop
|
|
|
|
/*
|
|
repl - the Lisp Read/Evaluate/Print loop
|
|
*/
|
|
void repl (object *env) {
|
|
for (;;) {
|
|
randomSeed(micros());
|
|
#if defined(printfreespace)
|
|
if (!tstflag(NOECHO)) gc(NULL, env);
|
|
pint(Freespace+1, pserial);
|
|
#endif
|
|
if (BreakLevel) {
|
|
pfstring(" : ", pserial);
|
|
pint(BreakLevel, pserial);
|
|
}
|
|
pserial('>'); pserial(' ');
|
|
Context = NIL;
|
|
object *line = read(gserial);
|
|
#if defined(CPU_NRF52840)
|
|
seriell->flush();
|
|
#endif
|
|
// Break handling
|
|
if (BreakLevel) {
|
|
if (line == nil || line == bsymbol(COLONC)) {
|
|
pln(pserial); return;
|
|
} else if (line == bsymbol(COLONA)) {
|
|
pln(pserial); pln(pserial);
|
|
GCStack = NULL;
|
|
longjmp(*handler, 1);
|
|
} else if (line == bsymbol(COLONB)) {
|
|
pln(pserial); printbacktrace();
|
|
line = bsymbol(NOTHING);
|
|
}
|
|
}
|
|
if (line == (object *)KET) error2("unmatched right bracket");
|
|
protect(line);
|
|
pfl(pserial);
|
|
line = eval(line, env);
|
|
pfl(pserial);
|
|
printobject(line, pserial);
|
|
unprotect();
|
|
pfl(pserial);
|
|
pln(pserial);
|
|
}
|
|
}
|
|
|
|
/*
|
|
loop - the Arduino IDE main execution loop
|
|
*/
|
|
void
|
|
loop()
|
|
{
|
|
if (!setjmp(toplevel_handler)) {
|
|
#if defined(resetautorun)
|
|
volatile int autorun = 12; // Fudge to keep code size the same
|
|
#else
|
|
volatile int autorun = 13;
|
|
#endif
|
|
if (autorun == 12) {
|
|
autorunimage();
|
|
}
|
|
}
|
|
|
|
ulisperror();
|
|
repl(NULL);
|
|
}
|
|
|
|
void ulisperror () {
|
|
// Come here after error
|
|
delay(100); while (seriell->available()) seriell->read();
|
|
clrflag(NOESC); BreakLevel = 0; TraceStart = 0; TraceTop = 0;
|
|
for (int i=0; i<TRACEMAX; i++) TraceDepth[i] = 0;
|
|
#if defined(sdcardsupport)
|
|
SDpfile.close(); SDgfile.close();
|
|
#endif
|
|
#if defined(lisplibrary)
|
|
if (!tstflag(LIBRARYLOADED)) { setflag(LIBRARYLOADED); loadfromlibrary(NULL); }
|
|
#endif
|
|
#if defined(ULISP_WIFI)
|
|
client.stop();
|
|
#endif
|
|
}
|