Skip to content

Commit 68cf5e8

Browse files
authored
Release 4.4b - 31st March 2023
1 parent 522aa42 commit 68cf5e8

File tree

2 files changed

+123
-102
lines changed

2 files changed

+123
-102
lines changed

ulisp-esp-comments.ino

+67-57
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/* uLisp ESP Release 4.4 - www.ulisp.com
2-
David Johnson-Davies - www.technoblogy.com - 21st March 2023
1+
/* uLisp ESP Release 4.4b - www.ulisp.com
2+
David Johnson-Davies - www.technoblogy.com - 31st March 2023
33
44
Licensed under the MIT license: https://opensource.org/licenses/MIT
55
*/
@@ -498,10 +498,15 @@ bool eqsymbols (object *obj, char *buffer) {
498498
object *arg = cdr(obj);
499499
int i = 0;
500500
while (!(arg == NULL && buffer[i] == 0)) {
501-
if (arg == NULL || buffer[i] == 0 ||
502-
arg->chars != (buffer[i]<<24 | buffer[i+1]<<16 | buffer[i+2]<<8 | buffer[i+3])) return false;
501+
if (arg == NULL || buffer[i] == 0) return false;
502+
int test = 0, shift = 24;
503+
for (int j=0; j<4; j++, i++) {
504+
if (buffer[i] == 0) break;
505+
test = test | buffer[i]<<shift;
506+
shift = shift - 8;
507+
}
508+
if (arg->chars != test) return false;
503509
arg = car(arg);
504-
i = i + 4;
505510
}
506511
return true;
507512
}
@@ -1003,7 +1008,7 @@ int8_t toradix40 (char ch) {
10031008
fromradix40 - returns the character encoded by the number n.
10041009
*/
10051010
char fromradix40 (char n) {
1006-
if (n >= 1 && n <= 9) return '0'+n-1;
1011+
if (n >= 1 && n <= 10) return '0'+n-1;
10071012
if (n >= 11 && n <= 36) return 'a'+n-11;
10081013
if (n == 37) return '-'; if (n == 38) return '*'; if (n == 39) return '$';
10091014
return 0;
@@ -1013,17 +1018,24 @@ char fromradix40 (char n) {
10131018
pack40 - packs six radix40-encoded characters from buffer into a 32-bit number and returns it.
10141019
*/
10151020
uint32_t pack40 (char *buffer) {
1016-
int x = 0;
1017-
for (int i=0; i<6; i++) x = x * 40 + toradix40(buffer[i]);
1021+
int x = 0, j = 0;
1022+
for (int i=0; i<6; i++) {
1023+
x = x * 40 + toradix40(buffer[j]);
1024+
if (buffer[j] != 0) j++;
1025+
}
10181026
return x;
10191027
}
10201028

10211029
/*
10221030
valid40 - returns true if the symbol in buffer can be encoded as six radix40-encoded characters.
10231031
*/
10241032
bool valid40 (char *buffer) {
1025-
if (toradix40(buffer[0]) < 11) return false;
1026-
for (int i=1; i<6; i++) if (toradix40(buffer[i]) < 0) return false;
1033+
int t = 11;
1034+
for (int i=0; i<6; i++) {
1035+
if (toradix40(buffer[i]) < t) return false;
1036+
if (buffer[i] == 0) break;
1037+
t = 0;
1038+
}
10271039
return true;
10281040
}
10291041

@@ -1058,8 +1070,8 @@ int checkbitvalue (object *obj) {
10581070
/*
10591071
checkintfloat - check that obj is an integer or floating-point number and return the number
10601072
*/
1061-
float checkintfloat (object *obj){
1062-
if (integerp(obj)) return obj->integer;
1073+
float checkintfloat (object *obj) {
1074+
if (integerp(obj)) return (float)obj->integer;
10631075
if (!floatp(obj)) error(notanumber, obj);
10641076
return obj->single_float;
10651077
}
@@ -1146,6 +1158,20 @@ int listlength (object *list) {
11461158
return length;
11471159
}
11481160

1161+
/*
1162+
checkarguments - checks the arguments list in a special form such as with-xxx,
1163+
dolist, or dotimes.
1164+
*/
1165+
object *checkarguments (object *args, int min, int max) {
1166+
if (args == NULL) error2(noargument);
1167+
args = first(args);
1168+
if (!listp(args)) error(notalist, args);
1169+
int length = listlength(args);
1170+
if (length < min) error(toofewargs, args);
1171+
if (length > max) error(toomanyargs, args);
1172+
return args;
1173+
}
1174+
11491175
// Mathematical helper functions
11501176

11511177
/*
@@ -2599,8 +2625,7 @@ object *sp_setf (object *args, object *env) {
25992625
It then returns result, or nil if result is omitted.
26002626
*/
26012627
object *sp_dolist (object *args, object *env) {
2602-
if (args == NULL || listlength(first(args)) < 2) error2(noargument);
2603-
object *params = first(args);
2628+
object *params = checkarguments(args, 2, 3);
26042629
object *var = first(params);
26052630
object *list = eval(second(params), env);
26062631
push(list, GCStack); // Don't GC the list
@@ -2635,8 +2660,7 @@ object *sp_dolist (object *args, object *env) {
26352660
It then returns result, or nil if result is omitted.
26362661
*/
26372662
object *sp_dotimes (object *args, object *env) {
2638-
if (args == NULL || listlength(first(args)) < 2) error2(noargument);
2639-
object *params = first(args);
2663+
object *params = checkarguments(args, 2, 3);
26402664
object *var = first(params);
26412665
int count = checkinteger(eval(second(params), env));
26422666
int index = 0;
@@ -2714,8 +2738,7 @@ object *sp_untrace (object *args, object *env) {
27142738
Returns the total number of milliseconds taken.
27152739
*/
27162740
object *sp_formillis (object *args, object *env) {
2717-
if (args == NULL) error2(noargument);
2718-
object *param = first(args);
2741+
object *param = checkarguments(args, 0, 1);
27192742
unsigned long start = millis();
27202743
unsigned long now, total = 0;
27212744
if (param != NULL) total = checkinteger(eval(first(param), env));
@@ -2756,9 +2779,7 @@ object *sp_time (object *args, object *env) {
27562779
Returns a string containing the output to the stream variable str.
27572780
*/
27582781
object *sp_withoutputtostring (object *args, object *env) {
2759-
if (args == NULL) error2(noargument);
2760-
object *params = first(args);
2761-
if (params == NULL) error2(nostream);
2782+
object *params = checkarguments(args, 1, 1);
27622783
object *var = first(params);
27632784
object *pair = cons(var, stream(STRINGSTREAM, 0));
27642785
push(pair,env);
@@ -2776,8 +2797,7 @@ object *sp_withoutputtostring (object *args, object *env) {
27762797
The optional baud gives the baud rate divided by 100, default 96.
27772798
*/
27782799
object *sp_withserial (object *args, object *env) {
2779-
object *params = first(args);
2780-
if (params == NULL) error2(nostream);
2800+
object *params = checkarguments(args, 2, 3);
27812801
object *var = first(params);
27822802
int address = checkinteger(eval(second(params), env));
27832803
params = cddr(params);
@@ -2799,8 +2819,7 @@ object *sp_withserial (object *args, object *env) {
27992819
to be read from the stream. The port if specified is ignored.
28002820
*/
28012821
object *sp_withi2c (object *args, object *env) {
2802-
object *params = first(args);
2803-
if (params == NULL) error2(nostream);
2822+
object *params = checkarguments(args, 2, 4);
28042823
object *var = first(params);
28052824
int address = checkinteger(eval(second(params), env));
28062825
params = cddr(params);
@@ -2828,8 +2847,7 @@ object *sp_withi2c (object *args, object *env) {
28282847
bitorder 0 for LSBFIRST and 1 for MSBFIRST (default 1), and SPI mode (default 0).
28292848
*/
28302849
object *sp_withspi (object *args, object *env) {
2831-
object *params = first(args);
2832-
if (params == NULL) error2(nostream);
2850+
object *params = checkarguments(args, 2, 6);
28332851
object *var = first(params);
28342852
params = cdr(params);
28352853
if (params == NULL) error2(nostream);
@@ -2870,8 +2888,7 @@ object *sp_withspi (object *args, object *env) {
28702888
*/
28712889
object *sp_withsdcard (object *args, object *env) {
28722890
#if defined(sdcardsupport)
2873-
object *params = first(args);
2874-
if (params == NULL) error2(nostream);
2891+
object *params = checkarguments(args, 2, 3);
28752892
object *var = first(params);
28762893
params = cdr(params);
28772894
if (params == NULL) error2(PSTR("no filename specified"));
@@ -4033,7 +4050,7 @@ object *fn_sqrt (object *args, object *env) {
40334050
}
40344051

40354052
/*
4036-
(number [base])
4053+
(log number [base])
40374054
Returns the logarithm of number to the specified base. If base is omitted it defaults to e.
40384055
*/
40394056
object *fn_log (object *args, object *env) {
@@ -4090,8 +4107,8 @@ object *fn_floor (object *args, object *env) {
40904107
}
40914108

40924109
/*
4093-
(truncate number)
4094-
Returns t if the argument is a floating-point number.
4110+
(truncate number [divisor])
4111+
Returns the integer part of number/divisor. If divisor is omitted it defaults to 1.
40954112
*/
40964113
object *fn_truncate (object *args, object *env) {
40974114
(void) env;
@@ -4102,8 +4119,8 @@ object *fn_truncate (object *args, object *env) {
41024119
}
41034120

41044121
/*
4105-
(round number)
4106-
Returns t if the argument is a floating-point number.
4122+
(round number [divisor])
4123+
Returns the integer closest to number/divisor. If divisor is omitted it defaults to 1.
41074124
*/
41084125
object *fn_round (object *args, object *env) {
41094126
(void) env;
@@ -4417,10 +4434,8 @@ object *fn_logxor (object *args, object *env) {
44174434
}
44184435

44194436
/*
4420-
(prin1-to-string item [stream])
4421-
Prints its argument to a string, and returns the string.
4422-
Characters and strings are printed with quotation marks and escape characters,
4423-
in a format that will be suitable for read-from-string.
4437+
(lognot value)
4438+
Returns the bitwise logical NOT of the value.
44244439
*/
44254440
object *fn_lognot (object *args, object *env) {
44264441
(void) env;
@@ -5226,7 +5241,7 @@ object *sp_error (object *args, object *env) {
52265241
Evaluates the forms with str bound to a wifi-stream.
52275242
*/
52285243
object *sp_withclient (object *args, object *env) {
5229-
object *params = first(args);
5244+
object *params = checkarguments(args, 1, 3);
52305245
object *var = first(params);
52315246
char buffer[BUFFERSIZE];
52325247
params = cdr(params);
@@ -5345,7 +5360,7 @@ object *fn_wificonnect (object *args, object *env) {
53455360
*/
53465361
object *sp_withgfx (object *args, object *env) {
53475362
#if defined(gfxsupport)
5348-
object *params = first(args);
5363+
object *params = checkarguments(args, 1, 1);
53495364
object *var = first(params);
53505365
object *pair = cons(var, stream(GFXSTREAM, 1));
53515366
push(pair,env);
@@ -6204,7 +6219,7 @@ const char doc131[] PROGMEM = "(exp number)\n"
62046219
"Returns exp(number).";
62056220
const char doc132[] PROGMEM = "(sqrt number)\n"
62066221
"Returns sqrt(number).";
6207-
const char doc133[] PROGMEM = "(number [base])\n"
6222+
const char doc133[] PROGMEM = "(log number [base])\n"
62086223
"Returns the logarithm of number to the specified base. If base is omitted it defaults to e.";
62096224
const char doc134[] PROGMEM = "(expt number power)\n"
62106225
"Returns number raised to the specified power.\n"
@@ -6214,10 +6229,10 @@ const char doc135[] PROGMEM = "(ceiling number [divisor])\n"
62146229
"Returns ceil(number/divisor). If omitted, divisor is 1.";
62156230
const char doc136[] PROGMEM = "(floor number [divisor])\n"
62166231
"Returns floor(number/divisor). If omitted, divisor is 1.";
6217-
const char doc137[] PROGMEM = "(truncate number)\n"
6218-
"Returns t if the argument is a floating-point number.";
6219-
const char doc138[] PROGMEM = "(round number)\n"
6220-
"Returns t if the argument is a floating-point number.";
6232+
const char doc137[] PROGMEM = "(truncate number [divisor])\n"
6233+
"Returns the integer part of number/divisor. If divisor is omitted it defaults to 1.";
6234+
const char doc138[] PROGMEM = "(round number [divisor])\n"
6235+
"Returns the integer closest to number/divisor. If divisor is omitted it defaults to 1.";
62216236
const char doc139[] PROGMEM = "(char string n)\n"
62226237
"Returns the nth character in a string, counting from zero.";
62236238
const char doc140[] PROGMEM = "(char-code character)\n"
@@ -6258,10 +6273,8 @@ const char doc155[] PROGMEM = "(logior [value*])\n"
62586273
"Returns the bitwise | of the values.";
62596274
const char doc156[] PROGMEM = "(logxor [value*])\n"
62606275
"Returns the bitwise ^ of the values.";
6261-
const char doc157[] PROGMEM = "(prin1-to-string item [stream])\n"
6262-
"Prints its argument to a string, and returns the string.\n"
6263-
"Characters and strings are printed with quotation marks and escape characters,\n"
6264-
"in a format that will be suitable for read-from-string.";
6276+
const char doc157[] PROGMEM = "(lognot value)\n"
6277+
"Returns the bitwise logical NOT of the value.";
62656278
const char doc158[] PROGMEM = "(ash value shift)\n"
62666279
"Returns the result of bitwise shifting value by shift bits. If shift is positive, value is shifted to the left.";
62676280
const char doc159[] PROGMEM = "(logbitp bit value)\n"
@@ -6811,6 +6824,7 @@ object *eval (object *form, object *env) {
68116824
pair = value(name, GlobalEnv);
68126825
if (pair != NULL) return cdr(pair);
68136826
else if (builtinp(name)) return form;
6827+
Context = NIL;
68146828
error(PSTR("undefined"), form);
68156829
}
68166830

@@ -7443,7 +7457,6 @@ object *nextitem (gfun_t gfun) {
74437457
if (ch == '.') valid = 0; else if (digitvalue(ch)<base) valid = 1; else valid = -1;
74447458
bool isexponent = false;
74457459
int exponent = 0, esign = 1;
7446-
buffer[2] = '\0'; buffer[3] = '\0'; buffer[4] = '\0'; buffer[5] = '\0'; // In case symbol is < 5 letters
74477460
float divisor = 10.0;
74487461

74497462
while(!issp(ch) && !isbr(ch) && index < bufmax) {
@@ -7494,8 +7507,7 @@ object *nextitem (gfun_t gfun) {
74947507
builtin_t x = lookupbuiltin(buffer);
74957508
if (x == NIL) return nil;
74967509
if (x != ENDFUNCTIONS) return bsymbol(x);
7497-
else if ((index <= 6) && valid40(buffer)) return intern(twist(pack40(buffer)));
7498-
buffer[index+1] = '\0'; buffer[index+2] = '\0'; buffer[index+3] = '\0'; // For internlong
7510+
if (index <= 6 && valid40(buffer)) return intern(twist(pack40(buffer)));
74997511
return internlong(buffer);
75007512
}
75017513

@@ -7568,9 +7580,7 @@ void initgfx () {
75687580
#endif
75697581
}
75707582

7571-
/*
7572-
setup - entry point from the Arduino IDE
7573-
*/
7583+
// Entry point from the Arduino IDE
75747584
void setup () {
75757585
Serial.begin(9600);
75767586
int start = millis();
@@ -7579,7 +7589,7 @@ void setup () {
75797589
initenv();
75807590
initsleep();
75817591
initgfx();
7582-
pfstring(PSTR("uLisp 4.4 "), pserial); pln(pserial);
7592+
pfstring(PSTR("uLisp 4.4b "), pserial); pln(pserial);
75837593
}
75847594

75857595
// Read/Evaluate/Print loop
@@ -7599,7 +7609,7 @@ void repl (object *env) {
75997609
pint(BreakLevel, pserial);
76007610
}
76017611
pserial('>'); pserial(' ');
7602-
Context = 0;
7612+
Context = NIL;
76037613
object *line = read(gserial);
76047614
if (BreakLevel && line == nil) { pln(pserial); return; }
76057615
if (line == (object *)KET) error2(PSTR("unmatched right bracket"));

0 commit comments

Comments
 (0)