CoCreate Modeling FAQ: Lisp programming in CoCreate Modeling

How to edit Lisp files?

There are a couple of editors which support syntax highlighting for Lisp. Some of them also can highlight / find / jump to opening and closing brackets which is one of the problems in Lisp for beginners (well not only for them wink ).

Here is a tiny list:

There are syntax highlighting files for ConTEXT (, UltraEdit and Notepad++. Notepad++ ships with CoCreate Modeling 2007 and later.

Wie schreibt man am besten Lisp-Dateien?

Es gibt eine Reihe von Editoren (Text-Bearbeitungs-Programmen), die auch die Programmiersprache Lisp unterstützen. Einige können auch das Auffinden von schliessenden und öffnenden Klammern erleichtern.

Eine kleine Liste von solchen Programmen findet sich hier:

Einige Editoren beherrschen automatische Syntaxhervorhebung, wie zum Beispiel ConTEXT (, UltraEdit and Notepad++. Notepad++ wird mit CoCreate Modeling 2007 und späteren Versionen ausgeliefert.

-- DerWolfgang, last updated 2007/04/05

How do I load a Lisp file into CoCreate Modeling?

There are several ways to do this:

  • By using the Common Lisp load command in the user input line: (load "c:/there/is/no/place/like/foo.lsp").
  • Via the file browser - click File/Open, change the file type to Lisp (*.lsp), then navigate to the file and open it.
  • By dragging the Lisp file from an Explorer window and dropping it over CoCreate Modeling.

If you have some Lisp code which you want CoCreate Modeling to load automatically during startup, you should place your Lisp code in one of the customization directories which CoCreate Modeling visits during startup. The choice of customization directory depends on whether you want the code to be loaded just for you, for your team, a site, or the whole company. For more details, start with the FAQ entry on automatic loading.

Wie lade ich eine Lisp-Datei in CoCreate Modeling?

Da gibt es viele Wege:

  • Indem man in der CoCreate Modeling-Eingabezeile das Common-Lisp-Kommando Lisp load benutzt: (load "c:/wo/auch/immer/foo.lsp").
  • Über den Dateibrowser - auf Datei/Öffnen klicken, den Dateityp auf Lisp (*.lsp) ändern, die Datei suchen und auswählen.
  • Indem man sich die Lisp-Datei in einem Explorer-Fenster greift und über CoCreate Modeling fallen läßt (drag&drop).

Hat man ein Stückchen Lisp-Code, das bei jedem Start automatisch von CoCreate Modeling geladen werden soll, kann man die Datei in einem der Anpassungsverzeichnisse ablegen, die CoCreate Modeling beim Start durchsucht. Welches Anpassungsverzeichnis man auswählt, hängt davon ab, ob der Code nur für einen einzelnen Anwender, eine Arbeitsgruppe, eine Firmenniederlassung oder für die ganze Firma geladen werden soll. Mehr dazu im FAQ-Eintrag zum automatischen Laden.

-- ClausBrod - 19 Nov 2006

(setf foo (- 1 0.01)) - Lisp says that foo is 0.010000000000000009. Is subtraction in Lisp buggy?

I've written a macro which produces a profile, but sometimes, depending on the input, the profile is reported as not closed.

Chances are that you are using floating-point calculations in your profile macro, and that you are running into inaccuracies which is a problem that every programmer encounters when calculating with floating-point values.

This kind of problem can be easily demonstrated by running the following two Lisp commands:

   (setf foo (-1 0.01))
   (display foo)

foo will displays its value as 0.010000000000000009 in the output box - but shouldn't that read 0.01? How can a simple subtraction like this fail?

In fact, this is not even a CoCreate Modeling or Lisp problem. A similar subtraction will fail in CoCreate Drafting as well:

  LET FOO ( 1 - (VAL '0.99'))
  DISPLAY (FOO > 0.01) {Result = 1}

So if both CoCreate Modeling and CoCreate Drafting fail in such a simple subtraction, maybe it's a CoCreate problem? Are their programmers too dumb to even get this right?

No. Similar code fails in other languages as well. Here's an example using VBscript (execute the script using wscript or cscript):

  Foo = 1 - 0.99
  If Foo > 0.01 Then
    WScript.Echo("Foo is larger than 0.01")
    WScript.Echo("Foo is equal to or smaller than 0.01")
  End if

The truth is that surprises like this can happen to you in any language and in any application. The reason is that the way in which CPUs represent floating-point values is imprecise by definition - there are only a limited number of bits available to store floating-point values. A good backgrounder on this is the famous white paper What Every Computer Scientist Should Know About Floating-Point Arithmetic. Alternatively, check out the (German) article at which is less formal and good enough as an introduction for most people.

Bottom line of those articles: Calculations with floating-point values are subject to inaccuracies because their representation in computer memory has only a finite number of "digits", and sometimes you simply cannot completely avoid floating-point errors. There are, however, techniques which reduce and control the error:

  • Work with tolerances: When comparing floating-point values, allow a certain margin of error, i.e. consider two floating-point values as equal if they differ by a certain epsilon value at most. (This is why we call this technique "Epsilontik", at least in German.)
  • Work with precise data types: Most programming languages provide two floating-point data types, i.e. single precision (C/C++: float, Lisp: short float) and double precision (C/C++: double). When in doubt, choose the more precise data type.
  • Avoid floating-point calculations wherever you can. Sometimes it is possible to do part of the calculations using integer arithmetic.
  • Avoid combining floating-point values in the same term if they are of completely different magnitudes. In a sequence of calculations, first add/subtract/whatever those floating-point pairs which are roughly in the same magnitude range.
  • Use algorithms which try to detect when two floating-point values really should be 100% identical, and which can "fix" the values on the fly.
  • Avoid algorithms which accumulate inaccuracies. For example, to rotate a transformation by 170 degrees, apply one 170 degress rotation rather than iteratively applying 17 rotations of 10 degrees.

-- ClausBrod

How can I use double-quotes in a literal Lisp string?

Imagine you want to replace all double-quote characters in a Lisp string using the Integration Kit function sd-string-replace. Seems we have a problem here: Literal strings in Lisp start with a double-quote character, and the next double-quote character in the string ends it. Hmmm...

The solution for this is to use so-called escape characters. In Lisp strings, backslashes are used as escape characters. (There are similar mechanisms in many programming languages, and the backslash character is used suspicously often for this purpose. Is there a secret backslash conspiracy going on here? big grin ) Example:

  (oli:sd-string-replace string "\"" "!")

This replaces all double-quote characters in string with exclamation marks. Note that two double-quote characters follow after the backslash. The special meaning of the first double-quote character ("end the current literal string") is ignored by the Lisp reader in this case because it is preceded by an escape character.

See also the section on the Single Escape Character in the Common Lisp HyperSpec.

Wie gebe ich in einem Lisp-String doppelte Anführungszeichen an?

Nehmen wir mal an, wir wollten in einem Lisp-String alle doppelten Anführungszeichen durch ein anderes Zeichen ersetzen, und zwar mit der Funktion sd-string-replace aus dem Integration Kit. Das könnte problematisch werden, denn ein Zeichenkettenliteral in Lisp beginnt mit einem doppelten Anführungszeichen, und das nächste Anführungszeichen beendet es. Hmmm...

Die Lösung sind die sogenannten ESCAPE- oder Abdeckzeichen. In Lisp-Zeichenketten wird der Rückstrich (Backslash) als solches Umschaltzeichen verwendet. (In anderen Programmiersprachen gibt es ähnliche Mechanismen, und das Backslash-Zeichen wird verdächtig oft für diesen Zweck eingesetzt - eine Backslash-Verschwörung? big grin ) Beispiel:

  (oli:sd-string-replace string "\"" "!")

Das Kommando ersetzt alle doppelten Anführungszeichen durch Ausrufezeichen. Man beachte, daß nach dem Backslash-Zeichen zwei doppelte Anführungszeichen folgen. Die besondere Bedeutung des ersten Anführungszeichen ("beende die aktuelle literale Zeichenkette") wird von Lisp in diesem Fall ignoriert, weil ein Backslash-Zeichen vorangeht.

Siehe auch den Abschnitt Single Escape Character in der HyperSpec-Dokumentation zu Common Lisp.

-- ClausBrod

How do I search for a backslash character in a string?

See the explanations above on escape characters to understand why the following would work:

  (oli:sd-string-match-pattern-p "*\\\\*" somestring)

Hint: Four backslashes are required in the pattern string because the pattern uses a special regular expression syntax in which the backslash has a special meaning, just like in Lisp. So you need to "escape" once because of the pattern language, and then another time because of Lisp.

Wie suche ich einen Rückstrich in einer Zeichenkette?

Siehe die Erläuterungen zum Thema Escape-Zeichen weiter oben; damit versteht man (hoffentlich), warum das folgende funktioniert:

  (oli:sd-string-match-pattern-p "*\\\\*" somestring)

Hinweis: Vier Rückstriche braucht man deswegen im Muster-String, weil das Muster in einer speziellen Syntax für reguläre Ausdrücke angegeben wird, in der der Rückstrich ebenso wie in Lisp eine spezielle Bedeutung hat. Man muß also einmal wegen der regulären Ausdrücke und dann noch einmal wegen Lisp die erforderlichen Rückstriche hinzufügen.

-- ClausBrod - 13 Jan 2005

What's the difference between setf and setq?

setf is a macro which builds on setq, but also allows to do funky stuff like this:

  (setf (fifth somelist) 42)

Note that the first argument isn't really an ordinary variable, but rather the description (as a Lisp form) of a place where the value is to be written to. lists which kind of forms can be used with setf.

In the majority of cases, this special evaluation isn't needed, so you're probably slightly better off (in terms of performance) by using setq. If you get it wrong, Lisp will kindly remind you with an error message, and then you can then still use setf instead when required.

Historically, setf is the abbreviation for setfq, which, according to Evolution of Lisp, stood for "quote the function and evaluate everything else".

Was ist der Unterschied zwischen setf und setq?

setf ist ein Makro, das auf setq aufbaut, zusätzlich aber ulkige Dinge wie dies hier erlaubt:

  (setf (fifth somelist) 42)

Das erste Argument von setf ist keine normale Variable, sondern eher die Beschreibung (als Lisp-Form) eines Platzes, an den der Wert geschrieben werden soll. listet auf, welche Arten von Lisp-Formen bei setf erlaubt sind.

In der Mehrzahl der Fälle braucht man solche speziellen Erweiterungen nicht, fährt also mit setq etwas besser (zumindest in Sachen Geschwindigkeit). Macht man's falsch, erinnert Lisp einen ohnehin freundlicherweise in Form von Fehlermeldungen daran, und dann kann man immer noch auf setf umsteigen.

Aus historischer Sicht ist setf eine Abkürzung für setfq, was laut Evolution of Lisp für "quote the function and evaluate everything else" steht.

-- ClausBrod

When I load my Lisp code, I get an "Unexpected end of input stream" error.

When the Lisp interpreter loads a file, it parses and checks it for syntax problems. One such syntax problem is when you open parentheses, but forget to close then. Example:

  (display "This will not work"

When saving this to a file called foo.lsp and loading it into CoCreate Modeling, you'll get an error message which reads like:

LISP error: Unexpected end of #<input stream "foo.lsp">.

What the Lisp interpreter is trying to say is that it arrived at the end of the file before all forms were closed properly, i.e. the number of opening parentheses does not match the number of closing parentheses. You can also run into this issue if you do not properly terminate a literal string, i.e. you forget the closing double-quote. So when you get this error message, open the Lisp file in an editor and look for missing parentheses and quotes.

A good Lisp editor will highlight matching parentheses, and also indent your code automatically so that any imbalances become readily apparent.

This error message also used to occur if the Lisp file contains a comment in the last line, and that last line does not end with a newline character. This has been fixed in CoCreate Modeling 2007.

Wenn ich meinen Lisp-Code lade, bekomme ich eine Fehlermeldung "Unexpected end of input stream"

Wenn der Lisp-Interpreter eine Datei lädt, prüft er sie auch auf Syntaxfehler. Ein solcher Syntaxfehler ist es, wenn man eine Klammer öffnet, aber sie zu schließen vergißt. Beispiel:

  (display "Das klappt nicht"

Speichert man das in eine Datei namens foo.lsp und lädt diese dann in CoCreate Modeling, bekommt man eine Fehlermeldung wie diese:

LISP error: Unexpected end of #<input stream "foo.lsp">.

Der Lisp-Interpreter versucht hier zu sagen, daß er am Ende der Datei angekommen ist, bevor alle Lisp-Formen ordentlich geschlossen wurden, daß also die Anzahl der öffnenden Klammern nicht der Anzahl der schließenden Klammern entspricht. Das Problem kann auch auftreten, wenn man eine literale Zeichenkette nicht ordentlich abschließt, also die Gänsefüßchen am Ende vergißt. Wenn man diese Meldung bekommt, öffnet man also einfach die betreffende Datei in einem Editor und sucht nach fehlenden Klammern oder Gänsefüßchen.

Ein guter Lisp-Editor hebt automatisch zugehörige Klammernpaare hervor und rückt auch automatisch den Code ein, so daß solche Strukturfehler sofort auffallen.

Die Fehlermeldung wurde bisher auch dann angezeigt, wenn die Lisp-Datei einen Kommentar in der letzten Zeile beinhaltet und diese letzte Zeile nicht mit einem Zeilenvorschub abgeschlossen ist. Dieses Problem ist in CoCreate Modeling 2007 behoben.

-- ClausBrod - 20 April 2005, last updated 21 December 2005

When I try to call an Integration Kit function SD-XXX, I get an error message: "The function SD-XXX is undefined"

First, check the spelling.

Second, all Integration Kit APIs are in the OLI LISP package. A Lisp package is the equivalent of a namespace in other languages. To specify the package, preprend the function name with OLI:, or add a use-package :oli statement at the beginning of your Lisp file.

   (SD-XXX param1 param2)      ;; package not specified, call might fail

   (OLI:SD-XXX param1 param2)  ;; MUCH better!

   (use-package :OLI)          ;; alternative method
   (SD-XXX param1 param2)

See the section on packages in the Lisp HyperSpec for more details. The introduction on packages in David Lamkins' Successful Lisp is also a good start to learn more about this topic.

Actually, in older versions of CoCreate Modeling it was sometimes to use IKIT APIs without specifying the OLI package. This was an undocumented side effect of our internal code structure; it just so happened that some code executed the required use-package call, or at least most of the time. Recent versions of CoCreate Modeling are less forgiving; to reduce the likelihood of name clashes, we now enforce proper specification of the OLI package.

Wenn ich die Integration-Kit-Funktion SD-XXX aufrufe, bekomme ich eine Fehlermeldung "The function SD-XXX is undefined"

Zuallererst: Korrekte Schreibweise des Kommandos überprüfen.

Zum zweiten: Alle APIs des Integration Kit befinden sich im Lisp-Package OLI. Ein Lisp-Package ist die Entsprechung zu Namensräumen in anderen Programmiersprachen. Um das Package anzugeben, hängt man vor den aufzurufenden Funktionsnamen einfach OLI:, oder aber man benutzt das Kommando use-package :oli am Anfang der Lisp-Datei.

   (SD-XXX param1 param2)      ;; Package nicht angegeben; das wird schiefgehen

   (OLI:SD-XXX param1 param2)  ;; Viel besser!

   (use-package :OLI)          ;; Alternative Methode
   (SD-XXX param1 param2)

Siehe auch den Abschnitt zu Packages in der Lisp-HyperSpec für detailliertere Erläuterungen. Die Einführung zu Packages im Buch Successful Lisp von David Lamkins ist auch ein guter Startpunkt, um mehr zu diesem Thema zu lernen.

In älteren Versionen von CoCreate Modeling konnte man übrigens manchmal straflos IKIT-APIs ohne Angabe des Packagenamens (OLI) aufrufen. Das war ein undokumentierter Seiteneffekt der internen Codestruktur; zufälligerweise benutzte interner Code den passenden Aufruf von use-package und nahm diese Anmeldung nicht zurück. Neuere Versionen von CoCreate Modeling sind weniger nachsichtig in diesem Punkt. Um die Wahrscheinlichkeit von Namenskollisionen zu reduzieren, erzwingt CoCreate Modeling seit einiger Zeit die korrekte Angabe des Packagenamens.

-- ClausBrod

When running my macro X, I get an error message reporting that "XXX cannot be coerced to YYY".

In the example displayed here, a function is called which expects a parameter of type double, but instead is passed a structure of type GPNTDOCU. So this type of error message usually indicates a programming error (incorrect parameter passing) in the Lisp code which has been executed.

As you can see from the screenshot, the error message itself is slightly buggy, too .-)

Wenn ich das Makro X ausführe, bekomme ich eine Fehlermeldung "XXX cannot be coerced to YYY".

Im Beispiel wird eine Funktion aufgerufen, die einen Parameter vom Typ double erwartet, stattdessen aber vom Aufrufer eine Struktur vom Typ GPNTDOCU erhält. Diese Art von Fehlermeldung deutet also auf einen Programmierfehler (falsche Parameterübergabe) im Lisp-Code hin, der gerade ausgeführt wurde.

Wie man dem Bild entnehmen kann, ist allerdings die Fehlermeldung selbst auch nicht ganz fehlerfrei .-)

-- ClausBrod

How do I port customization code from HP-UX to Windows?

If you only used the official APIs in the Integration Kit, there should rarely be a need to port anything. Almost all of them are cross-platform.

However, there are exceptions. One such exception is when you use sd-sys-background-job or sd-sys-exec to execute external commands. Such code will only continue to work if the external commands are available on the Windows platform as well.

In some cases, the HP-UX code will try to execute UNIX shell commands, such as grep, awk, sort, ls etc etc. For many external commands, you will actually find equivalent Integration Kit APIs or Lisp commands, which is the clearly preferable implementation. For instance, rather than using cp as an external command, use the sd-copy-file API; and to create a directory, sd-make-directory is more portable than mkdir. Consult the "Filing and Operating System" section of the Integration Kit documentation for details. Lisp also has a number of system-level commands, such as to access files, so the Common Lisp HyperSpec is also a good place to start your search.

Sometimes, however, you will still have to use external commands. The good news is that the DOS batch language (which can be used in sd-sys-exec commands on Windows) is actually quite flexible and powerful, even though the syntax differs significantly from UNIX systems. For example, grep can almost always be replaced by findstr, rm is the same as del, dir matches ls most of the time etc. A good book on the subject is Windows NT Shell Scripting by Timothy Hill. Also, there are many sources in the Internet which discuss batch file programming, such as Rob van der Woude's Scripting Pages.

Yet another alternative is to use one of the UNIX-lookalike tool collections and shell environments out there. These allow to continue to use grep, ksh and friends, so instead of porting your macros, you simply install a UNIX shell environment on your systems.

There are both commercial and free implementations. Examples:

Wie portiert man Anpassungen von HP-UX nach Windows?

Solange man nur die offiziellen APIs im Integration Kit verwendet, sollte es selten überhaupt einen Bedarf geben, irgendetwas zu portieren. Fast alle dieser APIs sind für alle Plattformen gültig.

Es gibt aber Ausnahmen. Eine solche Ausnahme ist es, wenn man sd-sys-background-job oder sd-sys-exec zum Ausführen externer Kommandos verwendet. Solcher Programmcode wird nur dann auf der neuen Plattform (in unserem Beispiel Windows) weiter funktionieren, wenn die externen Kommandos dort ebenfalls verfügbar sind.

In einigen Fällen versuchen HP-UX-Makros, UNIX-Kommandos auszuführen, beispielsweise grep, awk, sort oder ls. Für viele solcher externen Kommandos kann man übrigens Entsprechungen in Lisp selbst oder im Integration Kit finden - ganz klar ist das einer plattformabhängigen Programmierung vorzuziehen. Beispielsweise kann man statt cp als externem Kommando einfach sd-copy-file verwenden; zum Anlegen eines Verzeichnisses ist sd-make-directory portabler als mkdir. Der Abschnitt "Filing and Operating System" in der Dokumentation zum Integration Kit ist hier besonders interessant. Lisp stellt ebenfalls eine Reihe von Kommandos zur Verfügung, die von der verwendeten Plattform abstrahieren, beispielsweise zum Zugriff auf Dateien - die HyperSpec-Dokumentation zu Common Lisp ist also auch hier ein guter Anlaufpunkt.

Manchmal muß man aber doch auf externe Kommandos ausweichen. Die gute Nachricht ist hier, daß die Batch-Sprache von DOS (die man für mit sd-sys-exec ausgeführte Kommandos einsetzen kann) recht flexibel und vielseitig ist, auch wenn die Syntax doch oft erheblich von UNIX-Systemen abweicht. Beispielsweise kann man grep fast immer durch findstr ersetzen; rm ist fast identisch mit del, und dir entspricht meistens ls. Ein gutes Buch zu diesem Thema ist Windows NT Shell Scripting von Timothy Hill. Es gibt auch viele weitere Quellen im Internet, die Batch-Programmierung behandeln, beispielsweise die Scripting-Seiten von Rob van der Woude.

Eine weitere Alternative sind UNIX-ähnliche Werkzeugsammlungen und Shell-Umgebungen, die es erlauben, wie gewohnt grep, ksh und Kumpane zu verwenden. Anstatt also die Makros zu portieren, installiert man einfach eine solche Shell-Umgebung auf den Zielsystemen.

Es gibt sowohl kommerzielle als auch freie Implementierungen. Beispiele:

-- ClausBrod, last update 2007-04-03

How can I run CoCreate Drafting (ME10) macros in Annotator?

The IKIT function sd-execute-annotator-command is a convenient way to do this. For example, if you have an OSDD macro which takes two parameters, you can call it like this:

    :cmd (format nil "my_osdd_macro ~A ~A" param1 param2)) 

Note how format is used to build the full macro string before passing it to Annotator.

Wie kann ich Makros für CoCreate Drafting (ME10) in Annotation laufen lassen?

Das geht recht bequem über die IKIT-Funktion sd-execute-annotator-command. Wenn man zum Beispiel ein OSDD-Makro hat, das zwei Parameter erwartet, kann man es wie folgt rufen:

    :cmd (format nil "my_osdd_macro ~A ~A" param1 param2)) 

Man beachte, wie hier format benutzt wird, um den Makroaufruf aufzubauen, bevor man ihn an Annotator schickt.

-- ClausBrod - 11 Jun 2005

When asked for a TWiki account, use your own or the default TWikiGuest account.

to top

You are here: CoCreateModeling > OsdmFaq > OsdmFaqLisp

r1.21 - 24 Jul 2009 - 20:57 - ClausBrod to top

CoCreate ModelingRSS
  Operating system
  Memory management
  File handling
  App. knowhow
Code examples

  • My links
  • Show me topics of interest



TWiki Web TWiki Web Home Changes Topics Index Search

TWiki Webs Atari Blog Main OneSpaceModeling? Sandbox TWiki TWiki Webs Atari Blog Main OneSpaceModeling? Sandbox TWiki


Copyright © 1999-2018 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback