Translations of this page are also available in the following languages: Русский.

## Guile scripting

gEDA/gaf uses Guile Scheme to provide Scheme scripting capabilities, and all of the features of Guile are available to use. The Guile Reference Manual is available as an Info manual (`info guile` on most systems), or on the Guile website.

A collection of modules is provided for accessing and modifying gEDA objects and pages, called the gEDA Scheme API. The gEDA Scheme Reference Manual is also available as an Info manual (`info geda-scheme`).

### Tutorials

- Scripting a gnetlist backend in scheme (John Doty)

See also the Extending gschem section of the gEDA gschem User Guide, and Scheme Backend API section of the gEDA gnetlist User Guide.

### Reference documents

- gnetlist Scheme primitives
- Using REPL in gschem

### Scripting examples

You can download each script example and load it in gschem:

- just hit `:` and enter

      (load "filename.scm")

- then hit `Enter`

You can install them as well if you don't want to load them every time:

- copy the script you want into your `~/.gEDA` directory
- put the line

      (load "filename.scm")

  into your `~/.gEDA/gschemrc` (replace filename.scm with the real name of the script)

#### Removing objects with specific properties

For instance, let's remove all objects which are circles or arcs with zero radius:

remove-objects.scm

      (use-modules (geda page))

      ; Checks if the OBJECT is a circle or an arc with zero radius
      (define (zero-radius-object? object)
        (or
          (and (circle? object) (= (circle-radius object) 0))
          (and (arc?    object) (= (arc-radius    object) 0))))

      (apply page-remove! (active-page)
             (filter
               zero-radius-object?
               (page-contents (active-page))))

Let's suppose we have a component with a known attribute to remove, then we have to detach and remove all its attributes, too. The function below does exactly this.

remove-components-with-attribs.scm

      (use-modules (geda page))
      (use-modules (geda object))
      (use-modules (geda attrib))

      ; Removes all components having the attrib NAME=VALUE from PAGE
      (define (delete-components-by-attrib! page name value)
        (for-each
          (lambda (obj)
            (if (component? obj)
              (for-each
                (lambda (attr)
                  (and
                    (string=? (attrib-name attr) name)
                    (string=? (attrib-value attr) value)
                    (let ((attached-attribs (object-attribs obj)))
                      (apply detach-attribs! obj attached-attribs)
                      (apply page-remove! page obj attached-attribs))))
                (object-attribs obj))))
          (page-contents page)))

After loading the file, hit `:` and enter, for example,

    (delete-components-by-attrib! (active-page) "refdes" "R1")

#### Procedures for input-output

The following script defines two procedures that can be used in gaf shell batch scripts:

- `schematic-file->page`
- `page->schematic-file`

geda-io.scm

      (use-modules (ice-9 lineio))
      (use-modules (geda page))

      ; Input/output procedures
      ; reads FILE and outputs string
      (define (file->string file)
        (let* ((port (make-line-buffering-input-port (open-file file "r"))))
          (do ((line "" (read-string port))
               (s "" (string-append s line)))
            ((eof-object? line) ; test
             (close-port port)  ; expression(s) to evaluate in the end
             s)                 ; return value
            ; empty body
            )))

      ; reads schematic FILE and outputs PAGE object
      (define (schematic-file->page file)
          (string->page file (file->string file)))

      ; saves schematic PAGE to FILE
      (define (page->schematic-file page file)
        (with-output-to-file file
          (lambda () (display (page->string page)))))

#### Copy, move, and rotate objects

move-and-rotate.scm

      ; Scripting example by vzh per request of Kai-Martin Knaak :-)
      ; Use at your own risk.

      ; The main procedure here is
      ; multiple-copy-move-and-rotate-selection which can be abbreviated
      ; as mcmars.
      ; Usage:
      ;   launch gschem so it can use this script, e.g.
      ;     gschem -s move-and-rotate.scm
      ;   select objects in gschem, then hit ':' (semicolon) and type
      ;     (mcmars '(1000 . 500) 90 10)
      ;   hit <Enter>
      ; Enjoy!

      (use-modules (gschem selection))

      ; align coords by ALIGN
      (define (ceiling-coords vector align)
        (cons
          (* (ceiling-quotient (car vector) align) align)
          (* (ceiling-quotient (cdr vector) align) align)
          ))

      ; Get minimum X and minimum Y of two pairs of coords
      (define (min-coords coord1 coord2)
        (let ((x (min (car coord1) (car coord2)))
              (y (min (cdr coord1) (cdr coord2))))
          ; return value
          (cons x y)))

      ; Copy, move and rotate current selection. The selected objects
      ; are first copied, then translated by VECTOR and finally rotated
      ; by ANGLE about center which is calculated as rounded by 100
      ; lower left coordinate of all objects in selection.
      ; If no objects are selected, opens gschem message dialog with
      ; warning.
      ; Returns the copied objects.
      (define (copy-move-and-rotate-selection vector angle)
        (let ((objects (page-selection (active-page))))
          (if (null? objects)
            (gschem-msg "Select something first!")
            ; else
            (let* ((copied-objects (map copy-object objects))
                   (translated-objects (apply translate-objects! vector copied-objects))
                   (bounds (apply object-bounds translated-objects))
                   (rotation-center (ceiling-coords (min-coords (car bounds) (cdr bounds)) 100))
                   (rotated-objects (apply rotate-objects! rotation-center angle translated-objects)))
              (apply page-append! (active-page) rotated-objects)
              rotated-objects)
            )))

      ; Multiply VECTOR which must be a pair by NUMBER
      (define (multiply-vector-by vector number)
        (cons (* number (car vector)) (* number (cdr vector))))

      ; Copy, move and rotate current selection NUMBER times. Applies
      ; the copy-move-and-rotate-selection procedure multiple times
      ; increasing every time vector and angle by given values of VECTOR
      ; and ANGLE.
      ; If no objects are selected, opens gschem message dialog with
      ; warning.
      ; Return value is unspecified.
      (define (multiple-copy-move-and-rotate-selection vector angle num)
        (if (null? (page-selection (active-page)))
          (gschem-msg "Select something first!")
          ; else
          (do ((i num (1- i)))
            ((= i 0))
            (copy-move-and-rotate-selection
              (multiply-vector-by vector i) (* angle i)))
          ))

      ; Abbreviated name for the multiple-copy-move-and-rotate-selection
      ; procedure
      (define mcmars multiple-copy-move-and-rotate-selection)

#### Group attribute editing

Let's suppose you have selected several resistors' refdeses and want to rename them at once, e.g., if they were copy from another place.

set-selected-attribs-value

      (use-modules (gschem selection))

      (define (set-selected-attribs-value! value)
        (for-each
          (lambda (attrib)
            (set-attrib-value! attrib value))
          (page-selection (active-page))))

Usage of the procedure in gschem:

    (set-selected-attribs-value! "R100.?")

Now, after renumbering them using `T` `U`, you copy them all and want to rename those copied resistors appending a suffix:

append-selected-attribs-suffix.scm

      (use-modules (gschem selection))

      (define (append-selected-attribs-suffix! suffix)
        (for-each
          (lambda (attrib)
            (set-attrib-value!
              attrib
              (string-append (attrib-value attrib) suffix)))
          (page-selection (active-page))))

Usage of the procedure in gschem:

    (append-selected-attribs-suffix! "-top")

Now, let's rename some other attributes by adding a prefix:

append-selected-attribs-prefix.scm

      (use-modules (gschem selection))

      (define (append-selected-attribs-prefix! prefix)
        (for-each
          (lambda (object)
            (and (attribute? object)
                 (set-attrib-value!
                   object
                   (string-append prefix (attrib-value object)))))
          (page-selection (active-page))))

Usage of the procedure in gschem:

    (append-selected-attribs-prefix! "A1.")

Let's replace first letters of selected attribs with prefix:

append-selected-attribs-prefix.scm

      (use-modules (gschem selection))

      (define (replace-selected-attribs-prefix! prefix)
        (for-each
          (lambda (object)
            (and (attribute? object)
                 (set-attrib-value!
                   object
                   (string-append
                     prefix
                     (string-copy (attrib-value object) 1)))))
          (page-selection (active-page))))

Usage of the procedure in gschem:

    (replace-selected-attribs-prefix! "C")

Let's rename selected `netname=` attributes increasing them by a fixed number:

add-selected-attribs-number.scm

      (use-modules (gschem selection))

      (define (add-selected-attribs-number! number)
        (for-each
          (lambda (object)
            (and (attribute? object)
                 (set-attrib-value!
                   object
                   (number->string
                     (+ (string->number (attrib-value object)) number)))))
          (page-selection (active-page))))

Usage of the procedure in gschem:

    (add-selected-attribs-number! 100)

We could set any function instead of "+" on the net number in this procedure. For instance:

use-another-func.scm

      (use-modules (gschem selection))

      (define (use-another-func! func)
        (for-each
          (lambda (object)
            (and (attribute? object)
                 (set-attrib-value!
                   object
                   (number->string
                     (func (string->number (attrib-value object)))))))
          (page-selection (active-page))))

Usage of the procedure in gschem:

    (use-another-func! -)
    (define (multiply-by-2 x)
      (* 2 x))
    (use-another-func! multiply-by-2)

#### Moving objects using arrows

Let's define actions to move selected objects using `Shift` + arrow keys.

arrow-move.scm

      (use-modules (gschem selection))

      ; Default offset to move
      (define offset 100)

      ; Get moving vector
      (define (move-selection direction)
        (apply translate-objects!
          (case direction
            ((left ) (cons (- offset) 0))
            ((right) (cons (+ offset) 0))
            ((down ) (cons 0 (- offset)))
            ((up   ) (cons 0 (+ offset)))
            (else #f))
          (page-selection (active-page))))

      ; Define actions
      (define (&move-selection-left ) (move-selection 'left ))
      (define (&move-selection-right) (move-selection 'right))
      (define (&move-selection-down ) (move-selection 'down ))
      (define (&move-selection-up   ) (move-selection 'up   ))

      ; Define shortcuts
      (global-set-key "<Shift>Left"  '&move-selection-left)
      (global-set-key "<Shift>Right" '&move-selection-right)
      (global-set-key "<Shift>Up"    '&move-selection-up)
      (global-set-key "<Shift>Down"  '&move-selection-down)

The following script redefines current shortcuts so that if nothing is selected the canvas is moved with arrow keys (without `Shift` in this case), otherwise selected objects are moved.

arrow-move2.scm

      (use-modules (gschem selection))

      ; Default offset to move
      (define offset 100)

      ; Get moving vector
      (define (move-selection direction)
        (let ((selection (page-selection (active-page))))
          (if (null? selection)
            ; default behaviour
            (case direction
                ((left ) (&view-pan-left))
                ((right) (&view-pan-right))
                ((down ) (&view-pan-down))
                ((up   ) (&view-pan-up))
                (else #f))
            ; modified behaviour
            (apply translate-objects!
              (case direction
                ((left ) (cons (- offset) 0))
                ((right) (cons (+ offset) 0))
                ((down ) (cons 0 (- offset)))
                ((up   ) (cons 0 (+ offset)))
                (else #f))
              (page-selection (active-page))))
          ))

      ; Define actions
      (define (&move-selection-left ) (move-selection 'left ))
      (define (&move-selection-right) (move-selection 'right))
      (define (&move-selection-down ) (move-selection 'down ))
      (define (&move-selection-up   ) (move-selection 'up   ))

      ; Define shortcuts
      (global-set-key "Left"  '&move-selection-left)
      (global-set-key "Right" '&move-selection-right)
      (global-set-key "Up"    '&move-selection-up)
      (global-set-key "Down"  '&move-selection-down)