Uniot Core
0.8.1
Loading...
Searching...
No Matches
PrimitiveExpeditor.h
Go to the documentation of this file.
1/*
2 * This is a part of the Uniot project.
3 * Copyright (C) 2016-2020 Uniot <contact@uniot.io>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
26
32
33#pragma once
34
35#include <Arduino.h>
36#include <LispHelper.h>
38#include <setjmp.h>
39
40namespace uniot {
41using namespace lisp;
42
53class PrimitiveExpeditor {
54 class PrimitiveExpeditorInitializer;
59 struct PrimitiveDescription {
60 String name;
61 uint8_t argsCount;
62 Lisp::Type argsTypes[16];
63 Lisp::Type returnType;
64 };
65
66 public:
72 return sRegister;
73 }
74
84 static PrimitiveExpeditorInitializer describe(const String &name, Lisp::Type returnType, int argsCount, ...) {
85 sLastDescription.name = name;
86 sLastDescription.argsCount = argsCount;
87 sLastDescription.returnType = returnType;
88 memset(sLastDescription.argsTypes, 0, sizeof(sLastDescription.argsTypes));
89
90 if (argsCount > 0) {
91 va_list args;
92 va_start(args, argsCount);
93
94 for (auto i = 0; i < argsCount; i++) {
95 auto type = va_arg(args, int);
96 sLastDescription.argsTypes[i] = static_cast<Lisp::Type>(type);
97 }
98 va_end(args);
99 }
100
101 if (DescriptionModeGuard::isDescriptionMode()) {
102 longjmp(sDescriptionJumper, 1);
103 }
104 return {};
105 }
106
116 static PrimitiveDescription extractDescription(Primitive *primitive) {
117 volatile auto guard = DescriptionModeGuard(); // volatile to prevent compiler from optimization
118 if (primitive) {
119 if (setjmp(sDescriptionJumper) == 0) {
120 primitive(nullptr, nullptr, nullptr);
121 } else {
122 return sLastDescription;
123 }
124 }
125 return {};
126 }
127
133 return mRegisterProxy;
134 }
135
141 return length(list());
142 }
143
152 bool checkType(Object param, Lisp::Type expectedType) {
153 auto paramType = param->type;
154 switch (expectedType) {
155 case Lisp::Int:
156 if (paramType == TINT) {
157 return true;
158 }
159 break;
160
161 case Lisp::Bool:
162 if (paramType == TNIL || paramType == TTRUE) {
163 return true;
164 }
165 break;
166
167 case Lisp::BoolInt:
168 if (paramType == TINT || paramType == TNIL || paramType == TTRUE) {
169 return true;
170 }
171 break;
172
173 case Lisp::Symbol:
174 if (paramType == TSYMBOL) {
175 return true;
176 }
177 break;
178
179 case Lisp::Cell:
180 if (paramType == TCELL) {
181 return true;
182 }
183 break;
184
185 case Lisp::Any:
186 return true;
187
188 default:
189 break;
190 }
191 return false;
192 }
193
199 if (!mEvalList) {
200 mEvalList = eval_list(mRoot, mEnv, mList);
201 }
202 return mEvalList;
203 }
204
210 if (!mEvalList) {
211 return *mList;
212 }
213 return mEvalList;
214 }
215
223 return eval(mRoot, mEnv, obj);
224 }
225
231 void terminate(const char *msg) {
232 error("[%s] %s", mDescription.name.c_str(), msg);
233 }
234
241 void assertArgsCount(int length) {
242 if (getArgsLength() != length) {
243 error_wrong_params_number();
244 }
245 }
246
254 void assertArgs(uint8_t length, const Lisp::Type *types) {
255 if (getArgsLength() != static_cast<int>(length)) {
256 error_wrong_params_number();
257 }
258
259 auto param = evalList();
260 for (uint8_t i = 0; i < length; i++, param = param->cdr) {
261 if (!Lisp::correct(types[i])) {
262 error("[%s] Type for %d parameter is not set", mDescription.name.c_str(), i);
263 }
264 if (param == Nil) {
265 error("[%s] Unexpected end of params list at %d", mDescription.name.c_str(), i);
266 }
267 if (!checkType(param->car, types[i])) {
268 error_invalid_type(i, types[i], Lisp::getType(param->car));
269 }
270 }
271 }
272
280 void assertArgs(uint8_t length, ...) {
281 Lisp::Type types[length];
282
283 va_list args;
284 va_start(args, length);
285 for (uint8_t i = 0; i < length; i++) {
286 types[i] = static_cast<Lisp::Type>(va_arg(args, int));
287 }
288 va_end(args);
289
290 assertArgs(length, types);
291 }
292
298 assertArgs(mDescription.argsCount, mDescription.argsTypes);
299 }
300
308 Object getArg(int idx) {
309 const auto length = getArgsLength();
310 if (idx >= length) {
311 error("[%s] Trying to get %d arg out of %d", mDescription.name.c_str(), idx, length);
312 }
313
314 auto param = list();
315 for (auto i = 0; i < idx; i++, param = param->cdr);
316
317 return param->car;
318 }
319
328 bool getArgBool(int idx, bool acceptsInt = true) {
329 auto arg = getArg(idx);
330 if (!mEvalList) {
331 arg = evalObj(&arg);
332 }
333
334 auto type = acceptsInt ? Lisp::BoolInt : Lisp::Bool;
335 if (!checkType(arg, type)) {
336 error_invalid_type(idx, type, Lisp::getType(arg));
337 }
338
339 switch (arg->type) {
340 case TINT:
341 return arg->value != 0;
342 case TTRUE:
343 return true;
344 case TNIL:
345 default:
346 return false;
347 }
348 }
349
358 int getArgInt(int idx, bool acceptsBool = true) {
359 auto arg = getArg(idx);
360 if (!mEvalList) {
361 arg = evalObj(&arg);
362 }
363
364 auto type = acceptsBool ? Lisp::BoolInt : Lisp::Int;
365 if (!checkType(arg, type)) {
366 error_invalid_type(idx, type, Lisp::getType(arg));
367 }
368
369 switch (arg->type) {
370 case TINT:
371 return arg->value;
372 case TTRUE:
373 return 1;
374 case TNIL:
375 default:
376 return 0;
377 }
378 }
379
387 const char *getArgSymbol(int idx) {
388 auto arg = getArg(idx);
389
390 if (!checkType(arg, Lisp::Symbol)) {
391 error_invalid_type(idx, Lisp::Symbol, Lisp::getType(arg));
392 }
393
394 return arg->name;
395 }
396
404 Object makeBool(bool value) {
405 return value ? True : Nil;
406 }
407
414 Object makeInt(int value) {
415 return make_int(mRoot, value);
416 }
417
424 Object makeSymbol(const char *value) {
425 return make_symbol(mRoot, value);
426 }
427
428 private:
437 PrimitiveExpeditor(const PrimitiveDescription &description, Root root, VarObject env, VarObject list)
438 : mDescription(description), mRoot(root), mEnv(env), mList(list), mEvalList(nullptr), mRegisterProxy(description.name, &sRegister) {
439 }
440
448 void error_invalid_type(int idx, Lisp::Type expectedType, Lisp::Type actualType) {
449 error("[%s] Invalid type of %d parameter, expected <%s>, got <%s>", mDescription.name.c_str(), idx, Lisp::str(expectedType), Lisp::str(actualType));
450 }
451
455 void error_wrong_params_number() {
456 error("[%s] Wrong number of params", mDescription.name.c_str());
457 }
458
459 PrimitiveDescription mDescription;
460 Root mRoot;
461 VarObject mEnv;
462 VarObject mList;
463 Object mEvalList;
464
465 RegisterManagerProxy mRegisterProxy;
466 static RegisterManager sRegister;
467
468 static jmp_buf sDescriptionJumper;
469 static PrimitiveDescription sLastDescription;
470
477 class DescriptionModeGuard {
478 public:
482 DescriptionModeGuard() { sDescriptionMode = true; }
483
487 ~DescriptionModeGuard() { sDescriptionMode = false; }
488
494 static bool isDescriptionMode() { return sDescriptionMode; }
495
496 private:
497 static bool sDescriptionMode;
498 };
499
506 class PrimitiveExpeditorInitializer {
507 public:
516 PrimitiveExpeditor init(Root root, VarObject env, VarObject list) {
517 return {PrimitiveExpeditor::sLastDescription, root, env, list};
518 }
519 };
520};
521
522} // namespace uniot
Type
Enumeration of supported Lisp data types.
Definition LispHelper.h:94
@ BoolInt
Definition LispHelper.h:98
@ Symbol
Definition LispHelper.h:99
@ Cell
Definition LispHelper.h:100
@ Bool
Definition LispHelper.h:97
@ Int
Definition LispHelper.h:96
@ Any
Definition LispHelper.h:102
static bool correct(Lisp::Type type)
Checks if a type value is within the valid range.
Definition LispHelper.h:111
static Lisp::Type getType(lisp::Object obj)
Determines the Lisp::Type of a given Lisp object.
Definition LispHelper.h:138
static const char * str(Lisp::Type type)
Converts a type enumeration value to a human-readable string.
Definition LispHelper.h:120
Definition PrimitiveExpeditor.h:53
int getArgInt(int idx, bool acceptsBool=true)
Gets an integer value from an argument.
Definition PrimitiveExpeditor.h:358
RegisterManagerProxy & getAssignedRegister()
Gets the register proxy assigned to this expeditor.
Definition PrimitiveExpeditor.h:132
static PrimitiveDescription extractDescription(Primitive *primitive)
Extracts description metadata from a primitive function.
Definition PrimitiveExpeditor.h:116
static PrimitiveExpeditorInitializer describe(const String &name, Lisp::Type returnType, int argsCount,...)
Describes a primitive function by setting up its metadata.
Definition PrimitiveExpeditor.h:84
const char * getArgSymbol(int idx)
Gets a symbol value from an argument.
Definition PrimitiveExpeditor.h:387
Object makeBool(bool value)
Creates a boolean Lisp object.
Definition PrimitiveExpeditor.h:404
void terminate(const char *msg)
Terminates execution with an error message.
Definition PrimitiveExpeditor.h:231
Object evalObj(VarObject obj)
Evaluates a single object in the current environment.
Definition PrimitiveExpeditor.h:222
int getArgsLength()
Gets the number of arguments in the current function call.
Definition PrimitiveExpeditor.h:140
Object makeSymbol(const char *value)
Creates a symbol Lisp object.
Definition PrimitiveExpeditor.h:424
bool getArgBool(int idx, bool acceptsInt=true)
Gets a boolean value from an argument.
Definition PrimitiveExpeditor.h:328
Object getArg(int idx)
Gets an argument at the specified index.
Definition PrimitiveExpeditor.h:308
void assertDescribedArgs()
Asserts arguments against the primitive's description.
Definition PrimitiveExpeditor.h:297
void assertArgsCount(int length)
Asserts that the function was called with the expected number of arguments.
Definition PrimitiveExpeditor.h:241
void assertArgs(uint8_t length, const Lisp::Type *types)
Asserts that arguments match the expected types.
Definition PrimitiveExpeditor.h:254
Object makeInt(int value)
Creates an integer Lisp object.
Definition PrimitiveExpeditor.h:414
void assertArgs(uint8_t length,...)
Variadic version of assertArgs.
Definition PrimitiveExpeditor.h:280
Object evalList()
Evaluates the argument list if not already evaluated.
Definition PrimitiveExpeditor.h:198
Object list()
Gets the argument list (evaluated or not).
Definition PrimitiveExpeditor.h:209
static RegisterManager & getRegisterManager()
Gets the static register manager instance.
Definition PrimitiveExpeditor.h:71
bool checkType(Object param, Lisp::Type expectedType)
Checks if a parameter matches an expected Lisp type.
Definition PrimitiveExpeditor.h:152
Definition RegisterManager.h:37
Definition RegisterManagerProxy.h:35
struct Obj * Object
A pointer to a Lisp object structure.
Definition LispHelper.h:61
void * Root
A generic pointer representing the root of a Lisp environment.
Definition LispHelper.h:76
struct Obj ** VarObject
A pointer to a pointer to a Lisp object structure.
Definition LispHelper.h:69
Contains type definitions and utilities for interacting with the Lisp interpreter.
Contains descriptions and implementations of primitive functions for hardware interaction.
Definition LispPrimitives.cpp:21
Contains all classes and functions related to the Uniot Core.