I am attempting to use the #‹digit10›{1,8}= and #‹digit10›{1,8}# syntax as described in The Reader, but receive the read-syntax: `#...=` forms not enabled when I do so:
This code has been run a DrRacket REPL with only #lang racket in the edit area, and in the racket program through a terminal emulator with no further arguments or (require ...)s.
racket> (list #1=100 #1# #1#)
==> read-syntax: `#...=` forms not enabled
Reading states that the parameter read-accept-graph determines whether the reader will parse graph syntax. What's strange is that the parameter is currently set to #t.
racket> (read-accept-graph)
==> #t
The following confirms that read-accept-graph changes how the reader reacts to graph syntax:
racket> (parameterize ([read-accept-graph #t])
(read (open-input-string "(list #1=100 #1# #1#)")))
==> '(list 100 100 100)
racket> (parameterize ([read-accept-graph #f])
(read (open-input-string "(list #1=100 #1# #1#)")))
==> string::7: read: `#...=` forms not allowed in `read-syntax` mode
It seems that the problem is that read-accept-graph is bound to #f at the moment when racket is reading the expressions, even if it might be #t by the time those expressions are executed.
TLDR:
How do I parameterize read-accept-graph to #t before expressions containing graph structure are read?
The blunt answer to your question is that reader graph notation is not allowed in read-syntax mode, regardless of the current value of read-accept-graph.
However, your question helpfully uncovered two things:
The recent rewrite of the reader in Racket v7.0 broke the error message raised when encountering illegal graph notation. The first error should read `#...=` forms not allowed in `read-syntax` mode, and the second should read `#...=` forms not enabled, the reverse of what they currently produce.
The relevant section of the reference, Reading Graph Structure, does not properly document this difference between read and read-syntax.
I’ve pushed a fix for both of these problems in commit 2a667dc, which will be included in Racket v7.1.
Related
I love Racket's #;. I want to see it in every language that I ever use again. Can it be added to other Lisps via their macro systems? Or does the commenting character break the macro system's ability to read the code?
A sufficient answer will demonstrate a macro being built in any Lisp other than Racket that allows for a change in the commenting system. You need not actually implement Racket's #;, but I would like it if you do. Lisps with the least similarity to Racket, e.g. Clojure or any non-Scheme will be particularity nice to see.
#; isn't a macro, it's what Common lisp would call a readmacro: what it does is defined at read time, not later than that. Read macros which aim to completely suppress input are mildly perilous because there needs to be a way of saying 'read the following thing, but ignore it', and that's only possible if any other readmacros behave well: there's nothing to stop someone defining a readmacro which produces some side-effect even if reading is suppressed.
However, well-behaved readmacros (which includes all of the standard ones and the rest of the standard reader) in CL won't do that: they'll listen to whether reading is being suppressed, and behave accordingly.
CL allows you to do this as standard by using its conditionalisation on features, and in particular #+(or) <expr> will always skip <expr>.
But you can define your own: #; is not predefined so you can define it:
(set-dispatch-macro-character
#\# #\;
(lambda (stream char n)
(declare (ignore char))
(let ((*read-suppress* t))
(dotimes (i (or n 1) (values))
(read stream)))))
After this, or at least with a better tested version of this, then #; <expr> (or obviously #;<expr>) will read as whitespace, and #2; ... ... will skip two following expressions:
> (let ((x #;1 #2; 2 3 4)) x)
4
What you are looking for is #+(or) reader macro.
Since (or) evaluates to nil, the condition is always false the following form is never evaluated.
I'm trying to get a better understanding of how S-expressions are evaluated in different lisps and wanted to see they would handle interesting ill-formed expressions. I get that Common Lisp and Scheme are totally different languages, but is there a specific difference in their semantics that explains the difference in behavior. For example, Lisp-1s and Lisp-2s have observable differences in their behavior, as do hygienic vs non-hygienic macro systems.
I have a program containing an unreachable ill-formed if expression in Scheme and Common Lisp.
;; foo.scm
(if #t 1 (if))
(display "12")
And the Common Lisp version
;; foo.lisp
(if t 1 (if))
(display "12")
chicken and guile both produce a syntax error.
Chicken:
% chicken foo.scm
Syntax error: (foo.scm:1) in `if' - pair expected
(if)
Expansion history:
<syntax> (##core#begin (if #t 1 (if)))
<syntax> (if #t 1 (if))
<syntax> (##core#if #t 1 (if))
<syntax> (if) <--
Guile:
% guile foo.scm
...
.../foo.scm:1:9: source expression failed to match any pattern in form (if)
sbcl and clisp both print 12 and emit no warnings.
SBCL:
% sbcl --load foo.lisp
This is SBCL 1.3.11, an implementation of ANSI Common Lisp.
...
12
0]^D
CLISP
% clisp foo.lisp
"12"
Implementations of Common Lisp: Interpreter and Compilers
In Common Lisp the type of code execution depends on what the Lisp system implements and how you use it. Often Common Lisp implementations have multiple ways to execute code: an interpreter and one or more compilers. Even a single running implementation may have that and it will allow the user to switch between those.
Interpreter: executing directly from the Lisp data structure. It is not an interpreter of virtual machine code, like the JVM. One would not expect that the interpreter validates the full code tree/graph during execution. The interpreter usually looks only at the current top form it executes.
Compiler: compiles Lisp code to C, some Byte Code or Machine Code. Since the compiler generates code before the code runs, it will do a syntax check of all the code (for a possible exception, see the remark at the bottom) it sees.
Toplevel evaluation: may use an interpreter, a compiler or a mix of both.
GNU CLISP has both an interpreter and a compiler
Example in GNU CLISP:
LOAD of a text file typically uses the interpreter:
[1]> (load "test.lisp")
;; Loading file test.lisp ...
;; Loaded file test.lisp
T
The Interpreter will not detect the error, because it does not check the whole expression for syntactic correctness. Since the else clause with the syntax error is never used, the Interpreter will never look at it.
CLISP also has a compiler:
[2]> (compile-file "test.lisp")
;; Compiling file /tmp/test.lisp ...
** - Continuable Error
in #:|1 1 (IF T 1 ...)-1| in line 1 : Form too short, too few arguments: (IF)
If you continue (by typing 'continue'): Ignore the error and proceed
The following restarts are also available:
ABORT :R1 Abort main loop
As you see, the CLISP compiler detects the syntax error and gives a clear error message.
SBCL uses a compiler, but also has an interpreter
SBCL by default uses a compiler, which will detect the error. For top-level forms it uses for some forms a simpler evaluation mechanism. One can also switch to an interpreter.
If you write a simple IF form in SBCL, the evaluator does not use full compilation and doesn't catch the error:
CL-USER> (if t 1 (if))
1
If you write the same code inside a function definition, the error gets detected, because function definitions will be compiled by default:
CL-USER> (defun foo () (if t 1 (if)))
; in: DEFUN FOO
; (IF)
;
; caught ERROR:
; error while parsing arguments to special operator IF:
; too few elements in
; ()
; to satisfy lambda list
; (SB-C::TEST SB-C::THEN &OPTIONAL SB-C::ELSE):
; between 2 and 3 expected, but got 0
;
; compilation unit finished
; caught 1 ERROR condition
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO
If you would switch to the full SBCL interpreter, the error would not be detected at definition time:
CL-USER> (setf *evaluator-mode* :interpret)
:INTERPRET
CL-USER> (defun foo () (if t 1 (if)))
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO
Optimisation and syntax checking
Note that some optimising compilers might not check the syntax of unreachable code.
Summary
In most Common Lisp implementations you need to use the compiler to get full syntax warnings/errors.
Looks like the CL part is handled by nicely by Rainer. I just want to point out that R5RS are not required to act the way as your two implementations are.
R5RS is very underspecified and for if it only specifies what to do when used right we have this passage about error handling:
When speaking of an error situation, this report uses the phrase "an
error is signalled" to indicate that implementations must detect and
report the error. If such wording does not appear in the discussion
of an error, then implementations are not required to detect or report
the error, though they are encouraged to do so. An error situation
that implementations are not required to detect is usually referred to
simply as "an error."
So, to wrap this up in R5RS the string "banana" is just as correct as Guile and Chickens response to the evaluation of (if #t 1 (if)).
R6RS on the other hand states in general that when an exceptional situation is detected by the implementation, an exception is raised. This means that if with less than two expressions or more than 3 should raise an exception, but it doesn't say it has to happen compile time since the language might be parsing and interpreting as it goes.
This question already has answers here:
operator #+ and #- in .sbclrc
(2 answers)
Closed 6 years ago.
I am reading the cl-fad/load.lisp code tonight, and I found there are symbols #+: and #-: in the front of expression or string.
What's these symbols meaning?
These are a read-time conditionalization facility: #+ and #- let you decide what expression to read based on Feature Expressions.
E.g.,
#+:allegro (require :osi)
#+:sbcl (require :sb-executable)
means that when running under allegro, the module :osi will be loaded but when running under sbcl, the module :sb-executable will be loaded by require.
Under all other implementations require will not be called at all because read will skip over the forms.
You can check not just the implementation name, but a specific feature, e.g.,
#+(<= (integer-length most-positive-fixnum) 32)
code for a 32-bit lisp
#+(> (integer-length most-positive-fixnum) 32)
code for a 64-bit lisp
In addition to selecting code based on implementation, this allows one to easily "comment out" a section of your code (i.e., the next sexp):
#+(or) (this code will be skipped over by any lisp reader
because (or) returns nil)
This are reader macros based on the list features, this macros indicates to execute a form or not if the symbol is present on the features list
Showing the features list:
CL-USER> *features*
(:SWANK :QUICKLISP :QUICKLISP-SUPPORT-HTTPS :ROS.INIT :ASDF-PACKAGE-SYSTEM :ASDF3.1 :ASDF3 :ASDF2 :ASDF :OS-MACOSX :OS-UNIX :ASDF-UNICODE :PRIMARY-CLASSES :COMMON-LISP :OPENMCL :CCL :CCL-1.2 :CCL-1.3 :CCL-1.4 :CCL-1.5 :CCL-1.6 :CCL-1.7 :CCL-1.8 :CCL-1.9 :CCL-1.10 :CCL-1.11 :CLOZURE :CLOZURE-COMMON-LISP :ANSI-CL :UNIX :OPENMCL-UNICODE-STRINGS :IPV6 :OPENMCL-NATIVE-THREADS :OPENMCL-PARTIAL-MOP :MCL-COMMON-MOP-SUBSET :OPENMCL-MOP-2 :OPENMCL-PRIVATE-HASH-TABLES :STATIC-CONSES-SHOULD-WORK-WITH-EGC-IN-CCL :X86-64 :X86_64 :X86-TARGET :X86-HOST :X8664-TARGET :X8664-HOST :DARWIN-HOST :DARWIN-TARGET :DARWINX86-TARGET :DARWINX8664-TARGET :DARWINX8664-HOST :64-BIT-TARGET :64-BIT-HOST :DARWIN :LITTLE-ENDIAN-TARGET :LITTLE-ENDIAN-HOST)
In my case I 'm running:
CL-USER> (lisp-implementation-type)
"Clozure Common Lisp"
CL-USER> (lisp-implementation-version)
"Version 1.11-r16635 (DarwinX8664)"
Let's execute the form if I'm using CCL
CL-USER> #+CCL (1+ 1)
2
It works, because I have CCL on the features list
CL-USER> #-CCL (1+ 1)
; No value
It doest work because I have CCL in the features list
Or you can think the oppsite,only execute if I dont' have in teh features list
CL-USER> #-calimero (1+ 1)
2
You can ad any symbol :word to the features list and you can also add logic
let's execute if I'm on the CCL and using a darwin host (i.e MAC OS X)
CL-USER> #+(and ccl darwin-host) (1+ 1)
I'm trying to create a new language in Racket where statements are on separate lines. A newline defines the end of a statement and the start of a new one.
I read through the Create Languages chapter of the guide which was very useful but the examples were focused on extending s-exp-like languages. The only option I see is manually writing my own parser for read and read-syntax.
I was hoping to use readtables but I don't know if I can. I tried:
(make-readtable #f #f 'non-terminating-macro my-read-line-fn)
but I don't know if this is much help. I guess I could create a sub-readtable that does things like read-word, read-string which I dispatch to based on what character my-read-line-fn gets.
Is that the best strategy or is there a predefined way of reading until the end of line?
I don't think you need to do anything with the readtable. Your lang/reader.rkt can provide your own read-syntax that can read/parse however it wants, and presumably stop when it encounters EOL.
One interesting example is Brainfudge. Its concept of a "statement" is a single character, but IIUC also [ brackets ].
See its lang/reader.rkt and parser.rkt for the low-level bits, and then try to understand how that is ultimately evaluated as Racket expressions.
You do indeed need to write version of read and read-syntax that parse your language. The readtable is only meant to modify the builtin read, so I suggest that you take a look at Parser Tools (http://docs.racket-lang.org/parser-tools/index.html), which is tool for writing parsers in the lex/yacc style.
An alternative is to use ragg:
http://www.hashcollision.org/ragg/
Install Ragg using the package manager in DrRacket. Search for ragg in the list of available packages.
Make your own reader.rkt:
#lang s-exp syntax/module-reader
(test test)
#:read-syntax my-read-syntax
#:read my-read
;; override default read (won't be used but is required)
(define (my-read in) (read-line in))
;; override read-syntax by reading in one string at a time and
;; pass it to statement-string->code to get code as dara and
;; make it syntax with datum->syntax
(define (my-read-syntax in)
(datum->syntax #f (statement-string->code (read-line in))))
;; This is actually how you want your code
;; to become s-expressions. I imagine that my
;; module has a primitive called code that
;; interprets the string as is
(define (statement-string->code str)
(list 'code str))
Racket doesn't have "statements", so the concept of newlines ending "statements" is nonsensical.
If your motivation is to reduce or do away with parentheses, I encourage you to use a "standard alternative" reader like sweet-expressions, rather than making something home-grown.
I'm trying to go through "The Little Lisper" and already running into snags in the first chapter. I'm relatively new to Emacs (which has fueled my interest in learning Lisp and clojure). I downloaded the Mit-scheme app, and am working the exercises on Edwin.
I'm trying:
(atom? (cons a l))
where a is an atom and l is a list already defined. I get the following error:
;Unbound variable: atom?
Why? I have no problems using the "null?" function. I thought "atom?" is an internal function checking to see if the value returned is an atom.
Any explanation would be much appreciated. I still haven't set up my emacs to run scheme, and the minor differences between all the lisp dialects is testing my patience.
In "The Little Schemer" ("The Little Lisper"'s updated version) the atom? procedure is defined as follows (because atom? doesn't exist in Scheme):
(define (atom? x)
(and (not (null? x))
(not (pair? x))))
If you're following an old version of the book, I advise you to either look for a newer version or use the same programming language used in the book: Common Lisp for The Little Lisper, Scheme for The Little Schemer - and Racket is a great Scheme IDE to work with! take a look at this answer for some tips when going through The Little Schemer using Racket.
I'm trying to go through "The Little Lisper"...
I downloaded the Mit-scheme
Common Lisp and Scheme are very different languages.
You have to either use a different book (e.g., SICP) to match your language implementation or a different language implementation (e.g., clisp or sbcl) to match your book.
Take a look at the Scheme R5RS specification; it includes a list of functions and syntactic keywords. Although not part of the Scheme standard, mit-scheme has a function apropos that will find functions (other stuff) with a given name. try:
(apropos "atom")
(but it won't show anything, :-).
An atom is something that is not a 'cons cell' (generally, if I remember my CommonLisp). In Scheme you could implement one as:
(define (atom? thing) (not (pair? thing)))
Note: this definition of atom? is consistent with CommonLisp atom.