Doing something old with something new (2012 – 2017, 2020)
  • Common Lisp 51.6%
  • Go 46%
  • Makefile 0.5%
  • OMNeT++ MSG 0.5%
  • NewLisp 0.5%
  • Other 0.9%
Find a file
2025-02-23 15:20:22 +01:00
_morgue finally, make #'last do what it should 2023-06-21 08:55:09 +02:00
call moved subdir-Makefile to scripts/; also, small comment fix there 2017-04-07 06:57:07 +02:00
dep simplified terminal settings a bit 2020-07-12 11:54:30 +02:00
doc a few documentation improvements 2020-02-13 14:13:10 +01:00
eval better eval trace (commented out, but available) 2017-07-08 15:32:48 +02:00
fun fix builtin last bug 2024-03-14 11:41:03 +01:00
hlp bettered the listpusher and related changes 2017-10-15 14:34:04 +02:00
l Merge branch 'master' of https://git.w21.org/lisp/lingo 2024-03-04 13:32:35 +01:00
lio commented out a few Debug calls 2017-10-09 10:32:08 +02:00
lob fix: initialise readline only if stdin *and* stdout are terminals 2020-07-12 14:03:46 +02:00
msg setting unbound variables no longer an error, but warning 2017-08-13 00:30:04 +02:00
num new #'prime-numbers and tests 2022-04-26 09:58:14 +02:00
odep moved subdir-Makefile to scripts/; also, small comment fix there 2017-04-07 06:57:07 +02:00
olu go format decided to reformat my source differently 2020-06-11 10:34:20 +02:00
pre finally, make #'last do what it should 2023-06-21 08:55:09 +02:00
regtests new #'prime-numbers and tests 2022-04-26 09:58:14 +02:00
scripts scripts/find-deps.sh: GO111MODULE 2022-03-29 07:45:17 +02:00
sys setting unbound variables no longer an error, but warning 2017-08-13 00:30:04 +02:00
.gitignore don't keep the gen/ directory in the repository 2017-07-13 19:40:06 +02:00
INSTALL more explicitly check for missing programs in build 2020-07-13 17:42:55 +02:00
LICENSE updated copyright claim to current year 2017-09-14 09:52:45 +02:00
lin.go commented out a few Debug calls 2017-10-09 10:32:08 +02:00
lingo.png added logo lingo.png 2018-12-27 11:17:38 +01:00
Makefile Makefile: I hate it that Go changes its tooling and breaks my build 2025-02-23 15:20:22 +01:00
README a few documentation improvements 2020-02-13 14:13:10 +01:00
run-tests.lisp setting unbound variables no longer an error, but warning 2017-08-13 00:30:04 +02:00

-*- text -*-

This is the source code for a Lisp in Go ("lingo"), intended to get
me some more practice with Go and for toying around with a simple
Lisp interpreter. It is still (and will likely ever be) work in
progress and mostly undocumented.

lingo is copyrighted code. See the file LICENSE for details. It uses
(but does not include) the separately, but similarly licensed input
line editor "Liner" by Peter Harris; see
<https://github.com/peterh/liner>. The code is pulled in using "go
get".

See the file INSTALL for installation instructions.

If you want to find your way around it, see pre/*.lisp (code
incorporated into the interpreter executable and loaded on startup)
and the regtests/ and l/ directories for some Lisp code that works
with lingo. See DOCSTRINGS.txt (generated by the build) for
information on the builtin functions; if necessary, look at the
source in fun/builtins-*.go. In the interpreter, (doc SYMBOL) shows
the documentation of SYMBOL's function, (describe SYMBOL) shows its
other properties (in the general sense). The function (sys:symbols)
returns a list of symbols, as does (apropos-list "").

The dialect is mostly traditional, leaning towards Maclisp, with
lexical bindings (yay closures!) and overall rather simple. Some
names come from Common Lisp. The feature set is quite incomplete for
now.

See the doc/ directory for a few things I needed to write down
myself because I tended to forget them.

A few points, in parts deviations from traditional behaviour, bear
mentioning:

  Case-sensitive symbols
    For aesthetic reasons, symbols are not changed to all uppercase.
    Symbols whose names differ with respect to upper-/lowercase are
    considered different. The standard symbols are all in lowercase.

  Go-like format strings
    Because I wasn't too keen on implementing Common Lisp's rather
    complex feature set of the format function, the format string is
    passed to Go's fmt.Sprintf() function, which stands in the
    printf() tradition of C. While I have to admit this is mostly
    due to lazyness, it may have the upside that some today, who are
    not dyed-in-the-wool Lisp programmers, are more familiar with
    it. (I certainly am.) By the way, the %v format works for all
    Lisp objects.

  No longer: Mandatory variable declarations
    It used to be required that, before you assigned a value to a
    variable, it either be bound as a function parameter (or similar
    constructs like let), or be declared as a global variable using
    defvar or defparameter, both of which can also be used to assign
    an initial value. Setting a variable not predeclared this way
    was an error.

    I changed that because it had shown to be inconvenient for the
    quick line of code in the REPL. Instead, a WARNING is issued,
    similar to what SBCL does. The concept of warnings at all is
    new; they can be turned into errors by setting the -W command
    line option. (This is done by the regtests so we can catch and
    peruse the warnings.) The (currently) only other case where a
    warning is issued is when a builtin function is redefined.

  Generalized callables
    Like functions, a few other object types may be directly called
    with arguments. This is the conventional function call form:
    > (<function> &rest args) => value

    Now, we also have these:
    > (<vector> index)           => value
    > (<vector> index new-value) => new-value (also sets new value)
    > (<table> key)              => value
    > (<table> key new-value)    => new-value (also sets new value)
    > (<regexp> string)          => list-of-matches

    This idea came from Arc, which also has tables callable like
    functions. Pity they don't work with setf and friends as they
    are not recognizable at the time a macro is expanded.

  Function evaluation
    If the object in function position is not a symbol with a
    function definition, it will be evaluated to get a function
    (i.e. actually a callable). This means that things like

      ((make-function) 3 4 5)

    will work if (make-function) returns something that can be used
    as a function, also a symbol with a function (or some other
    callable) in the value cell instead of the function cell. This
    feels a bit more natural for vectors or tables than it does for
    functions. (I know this is, while convenient, dangerously close
    to Lisp-1 territory.)

  Limited numeric capabilities
    Numbers are essentially all 64-bit floats. There is no
    distiction between integers and floats, and there are no
    bignums, rationals, or complex numbers. While all these things
    are interesting and nice to have, I consider the implementation
    effort as too big to be really fun. Besides, I don't have any
    real knowledge of numerics and wouldn't use lingo (except as the
    simple interactive RPN calculator in l/lic, which I use all the
    time) for numerical applications anyway.

  Lambda lists
    In lambda lists, lingo supports ordinary positional parameters
    as well as &optional, &rest, and &key parameters, but not &aux,
    keyword-name, &allow-other-keys, or a supplied-p parameter. This
    is due to ease and efficiency of the implementation (this is a
    toy interpreter after all) as well as to me perceiving these
    things not as particularly necessary and aesthetic. That said, I
    do find &optional, &rest, and &key parameters useful enough to
    support them, but a function that has all of these will still
    seem ugly.

  Destructuring bind
    In a let, the left side of a bind clause can not only be a
    symbol, but also a list of symbols -- a proper list, or one with
    a non-nil end. In this case, the value (which must be a list) is
    mapped to the symbol list and bound accordingly. If the value
    list is longer than the symbol list and the symbol list is a
    proper list, only so many from the value list are used as there
    are symbols to bind them to. Example:

        (let (((a b) '(3 4 5 6)))
          (cons a b))
        => (3 . 4)

    If the symbol list ends not with nil, but with a symbol, all
    remaining values are bound to that symbol as a list. Example:

        (let (((a b . c) '(3 4 5 6)))
          (list a b c))
        => (3 4 (5 6))

    If the value list is shorter than the symbol list, all symbols
    for which there are no values are bound to nil. Example:

        (let (((a b . c) '(3)))
          (list a b c))
        => (3 nil nil)

    It is an error in this case if the value is not a list.

Regarding the source code -- well, there is little documentation,
because writing documentation wouldn't have been as much fun as
writing the code. Good luck, and have fun!

For bug reports and feature ideas, see the issue tracker at
https://gitlab.com/jyrgenn/lingo/issues