Недокументированные ограничения 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 кб. А если больше - то файл тупо обрубается по этой границе, наплевав на его корректность. Причем файл открывается, считывается, обрубается, автоматом обрубается и записывается, и никакие дальнейшие действия уже ничего не меняют: на выходе некорректный файл проекта, живите с этим.

Вроде что вспомнил, рассказал ;)



Поделитесь своим мнением


Я не робот.