Недокументированные ограничения AutoLISP
Зацепился сегодня языками с одним очень умным человеком (последнее время появляется здесь) на предмет ограничений в AutoLISP. Хотя б под AutoCAD. Чего знаю / помню - под катом.
Тот факт, что AutoLISP до сих пор остается 32-битным, далеко не секрет. А вот тот факт, что при таких приколах можно запросто получить переполнение того же int, как правило, забывается. Словить такую ошибку тяжело, но не невозможно
Это когда
1 2 3 4 | _$ (setq count (1- (expt 2 31))) 2147483647 _$ (setq count (1+ count)) -2147483648 |
Но это так, смазочка. Сказочка впереди.
Любая логическая конструкция в лиспе начинается со скобки и скобкой же заканчивается. Так вот, прикол: внутри одной логической конструкции не может быть больше 256 внутренних логических конструкций. Точнее, не так. Может и быть больше, но стабильность кода в таком случае под баальшим вопросом. К примеру:
1 2 3 4 5 6 7 8 | (defun c:some-command() ;; конструкция верхнего уровня (command ...) ;; вложенная конструкция 1 (command ...) ;; вложенная конструкция 2 ... (command ...) ;; вложенная конструкция 255 (command ...) ;; вложенная конструкция 256 (command ...) ;; вложенная конструкция 257 ) |
Такой код может работать корректно. Или нет. А вот код типа
1 2 3 4 5 6 7 8 9 10 11 12 | (defun c:some-command() // конструкция верхнего уровня (progn // вложенная конструкция 1 (command ...) ;; вложенная конструкция 1.1 (command ...) ;; вложенная конструкция 1.2 ... (command ...) ;; вложенная конструкция 1.255 (command ...) ;; вложенная конструкция 1.256 ) (progn // вложенная конструкция 2 (command ...) ;; вложенная конструкция 2.1 ) ) |
Сработает с вероятностью 100%. Для особо упоротых типа меня количество вложенных конструкций вообще можно ограничить 250 ![]()
Вместо progn может быть в принципе что угодно - if, cond, foreach, lambda - фиолетово. Главное, чтоб внутри тоже было не больше 256 (ну или сколько там себе положили) вложенных конструкций. Если идет вызов какой-то функции, то это считается как одна вложенная конструкция, и счетчик "обнуляется" при входе в вызываемую функцию.
По заявлениям разработчиков nanoCAD, у них такой проблемы нет. Ну хрен его знает, я не проверял.
Следующее, с чем я в свое время столкнулся - это уровни вложенности if. Не больше 9 штук:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | (if <...> ;; 1 (if <...> ;; 2 (if <...> ;; 3 (if <...> ;; 4 (if <...> ;; 5 (if <...> ;; 6 (if <...> ;; 7 (if <...> ;; 8 (if <...> ;; 9 (if <...> ;; Начиная отсюда возможны ошибки любого класса. Вплоть до (+ 2 2) будет давать 10 ) ) ) ) ) ) ) ) ) ) |
Код может сработать, если грузить его некомпилированным. А может и вывалить ошибку. Кому охота рисковать - останавливать не стану.
Ограничения по поводу количества вложенных конструкций и if-ов касаются только варианта "все внутри одной функции". А вот с обработкой ошибок совсем весело.
Вложенность обработки / отлова ошибок вообще не должны иметь уровень вложенности больше 9. Независимо от того, в какой функции они вызываются. Наличие 10-го уровня вложенности может просто положить код. Но если неохота получить слабопредсказуемый код, то отлов лучше все оставить "на самом верху". Т.е. вместо портянки с 10+ уровнями вложенности типа:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | (defun fun1 () ;; Ур.2 (vl-catch-all-apply (function (lambda () (fun2) ) ) ) ) (defun fun2 () ;; Ур.3 (vl-catch-all-apply (function (lambda () (fun3) ) ) ) ) (defun fun3 () ;; Ур.4 (vl-catch-all-apply (function (lambda () (fun4) ) ) ) ) (defun fun4 () ;; Ур.5 (vl-catch-all-apply (function (lambda () (fun5) ) ) ) ) (defun fun5 () ;; Ур.6 (vl-catch-all-apply (function (lambda () (fun6) ) ) ) ) (defun fun6 () ;; Ур.7 (vl-catch-all-apply (function (lambda () (fun7) ) ) ) ) (defun fun7 () ;; Ур.8 (vl-catch-all-apply (function (lambda () (fun8) ) ) ) ) (defun fun8 () ;; Ур.9 (vl-catch-all-apply (function (lambda () (fun9) ) ) ) ) (defun fun9 () ;; Ур.10 (vl-catch-all-apply (function (lambda () (/ 50 0) ) ) ) ) (defun fun-main () ;; Ур.1 (vl-catch-all-apply (function (lambda () (fun1) ) ) ) ) |
Может оказаться значительно более выгодным прописывать нечто типа:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | (defun fun1 () (fun2) ) (defun fun2 () (fun3) ) (defun fun3 () (fun4) ) (defun fun4 () (fun5) ) (defun fun5 () (fun6) ) (defun fun6 () (fun7) ) (defun fun7 () (fun8) ) (defun fun8 () (fun9) ) (defun fun9 () (/ 50 0) ) (defun fun-main (/ err) (if (vl-catch-all-error-p (setq err (vl-catch-all-apply (function (lambda () (fun1) ) ) ) ) ) (alert (vl-catch-all-error-message err)) ) ) |
Отловить ошибку станет тяжелее, конечно, но зато код работать будет вменяемее.
Ну а про вложенные метки начала-конца отмены я уже разорялся ![]()
Слегка дополню по поводу компиляции и файлов проектов (что для fas, что для vlx). Понятно, что в текущих реалиях актуальность проблемы вызывает массу вопросов, но, чтоб два раза не вставать...
Файл проекта для fas/vlx (любого типа) можно легко сформировать программно. Хоть лиспом, хоть чем. И даже все скомпилируется. До тех пор, пока файл не будет открыт через VLIDE. Я не знаю, где и как это прописано, но файл проекта не может быть объемом больше 64 кб. А если больше - то файл тупо обрубается по этой границе, наплевав на его корректность. Причем файл открывается, считывается, обрубается, автоматом обрубается и записывается, и никакие дальнейшие действия уже ничего не меняют: на выходе некорректный файл проекта, живите с этим.
Вроде что вспомнил, рассказал ![]()