Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

clisp check if string is a number

138 views
Skip to first unread message

David Hume

unread,
Apr 9, 2014, 12:58:06 PM4/9/14
to
I am testing to see if a string contains a number. If it does, I want to
process it differently from other strings.

I have this clisp code:

(setq par (read-from-string e))
(if (numberp par)
(progn
(setq stack (cons par stack))
)
)

If the string e is a grave character, i.e. e="`" I get an error from
read-from-string:

(read-from-string "`")

*** - READ: input stream #<INPUT STRING-INPUT-STREAM> ends within an object

How can I stop this error? I think there are other characters or
sequences of characters which cause problems too. This is just an
example.

Pascal J. Bourguignon

unread,
Apr 9, 2014, 1:36:13 PM4/9/14
to
You could remove #\` from the readtable.

This answer will probably be useless to you. This means that you've
not thought enough about your problem:

What does it mean for a string to "contain" a "number"?
What does READ-FROM-STRING mean?
Do you really mean NUMBERP?
Do you know all the standard syntaxes for numbers?
(ex.: 1 2. HELLO #|yes, it's a number syntax!!!|#
3.14e15 1/2 #c(1 2) #xabc #b1010 #o777)
What about non standard syntaxes?


--
__Pascal Bourguignon__
http://www.informatimago.com/
"Le mercure monte ? C'est le moment d'acheter !"

tfardy

unread,
Apr 9, 2014, 1:57:09 PM4/9/14
to
David Hume <david...@example.com> wrote:
> I am testing to see if a string contains a number. If it does, I want to
> process it differently from other strings.
>
> I have this clisp code:
>
> (setq par (read-from-string e))
> (if (numberp par)
> (progn
> (setq stack (cons par stack))
> )
> )
>
> If the string e is a grave character, i.e. e="`" I get an error from
> read-from-string:
>
> (read-from-string "`")
>
> *** - READ: input stream #<INPUT STRING-INPUT-STREAM> ends within an object
>
> How can I stop this error?

http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_fro.htm

See the meaning of eof-error-p.



--
pozdro
tfardy

tar...@google.com

unread,
Apr 9, 2014, 2:06:34 PM4/9/14
to
On Wednesday, April 9, 2014 9:58:06 AM UTC-7, David Hume wrote:
| If the string e is a grave character, i.e. e="`" I get an error from
| read-from-string:
|
| (read-from-string "`")
| *** - READ: input stream #<INPUT STRING-INPUT-STREAM> ends within an object
|
| How can I stop this error? I think there are other characters or
| sequences of characters which cause problems too.

Well, the simple hackish solution is to use

(ignore-errors (read-from-string ...))

But Pascal's answer raises a number of additional issues that you would want to be aware of.

Note that you might also want to bind *read-eval* to NIL when reading this input.

What is the larger problem you are trying to solve with this code?

David Hume

unread,
Apr 9, 2014, 2:13:26 PM4/9/14
to
Pascal J. Bourguignon wrote:

>
> You could remove #\` from the readtable.
>
> This answer will probably be useless to you. This means that you've
> not thought enough about your problem:
>
> What does it mean for a string to "contain" a "number"?
> What does READ-FROM-STRING mean?
> Do you really mean NUMBERP?
> Do you know all the standard syntaxes for numbers?
> (ex.: 1 2. HELLO #|yes, it's a number syntax!!!|#
> 3.14e15 1/2 #c(1 2) #xabc #b1010 #o777)
> What about non standard syntaxes?
>
>

As you seem to have grasped, I am a bit of a novice. What I have done is
written an rpn calculator. It is mostly working. It allows me to do
things like this:

2 3 4 * +
(14)
2 v 3 v * d *
(14 5.999999)
2l0 v
(14 5.999999 1.4142135623730950488L0)

where v means square-root, d means duplicate etc.

So it is mostly working fine. But to do it I had to tell the difference
between a number, and something else, because I wanted numbers to go
onto the stack.

I got to numberp of read-from-string largely by trial and error. If I
use numberp("2") for example, it says NIL. So I had to make the string
into an object first, and then see if that object was a number. Is that
the right terminology? I expect I need to block special characters from
my read.

If I enter this:

"

*** - READ: input stream #<INPUT STRING-INPUT-STREAM> ends within a string


David Hume

unread,
Apr 9, 2014, 2:16:37 PM4/9/14
to
tfardy wrote:

>
> http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_fro.htm
>
> See the meaning of eof-error-p.
>

I visited that page before posting but it is a bit brief, there are no
examples of the usage of eof-error-p.

David Hume

unread,
Apr 9, 2014, 2:27:55 PM4/9/14
to
tar...@google.com wrote:

> Well, the simple hackish solution is to use
>
> (ignore-errors (read-from-string ...))

That worked! Thanks.

>
> But Pascal's answer raises a number of additional issues that you would want to be aware of.
>
> Note that you might also want to bind *read-eval* to NIL when reading this input.
>

What would that do? I am finding generally that googling for answers
yields only brief descriptions with no examples. I should probably read
all the books before trying to code anything.

Pascal J. Bourguignon

unread,
Apr 9, 2014, 3:29:22 PM4/9/14
to
You have to make a choice:

- either you live in the lisp paradize,
- or you live in the other languages hell.


Living in the lisp paradize means that you just work with lisp data,
without caring about reading and lexical analysis, relying on the lisp
reader to do that for you.

Living in the other languages hell means that you will have to write
your own scanner and parser for the language you want to process. It
also means that it will be more difficult to mix lisp and this other
language in the same expression.


If you want to live in the lisp paradize, then forget about reading,
just use lisp sexps:

(defmacro def-rpn-fun (name parameters &body body)
`(progn
(setf (get ',name 'arity) ,(length parameters))
,(if (not (eq (find-package "COMMON-LISP")
(symbol-package name)))
`(defun ,name ,parameters ,@body)
`',name)))

(def-rpn-fun d (x) (values x x))
(def-rpn-fun v (x) (values (sqrt x)))
(def-rpn-fun + (x y) (values (+ x y)))
(def-rpn-fun - (x y) (values (- x y)))
(def-rpn-fun * (x y) (values (* x y)))
(def-rpn-fun / (x y) (values (/ x y)))
(def-rpn-fun tr (x y) (truncate x y))

(defun rpn (expr &optional stack)
(cond
((null expr) stack)
((numberp (first expr))
(rpn (rest expr) (cons (first expr) stack)))
((and (symbolp (first expr))
(fboundp (first expr))
(get (first expr) 'arity))
(rpn (rest expr)
(nreconc (multiple-value-list
(apply (first expr)
(loop :repeat (get (first expr) 'arity)
:collect (pop stack))))
stack)))
(t (error "invalid rpn ~S" expr))))

(assert (equal (rpn '(2 3 4 * +)) '(14)))
(assert (equal (rpn '(2 v 3 v * d *)) '(5.999999)))
(assert (equal (rpn '(210 v)) '(14.491377)))

(assert (equal (rpn '(2 4 -)) '(2)))
(assert (equal (rpn '(2 6 /)) '(3)))
(assert (equal (rpn '(3 10 tr)) '(1 3)))


(assert (equal (+ (first (rpn '(2 3 4 * *))) 100) 124))
(assert (equal (rpn (list 2 (/ 6 2) (* 2 2) '* '*)) '(24)))




If you want to scan and parse (this is quite funny actually), then study
the Dragon Book and write your scanner or parser (you may use a scanner
generator and a parser generator).



Anything else is a kludge.

tar...@google.com

unread,
Apr 9, 2014, 7:13:49 PM4/9/14
to
On Wednesday, April 9, 2014 11:27:55 AM UTC-7, David Hume wrote:
| Tom Russ wrote:

|| Note that you might also want to bind *read-eval* to NIL when reading
|| this input.

| What would that do?

It's more what it doesn't do. :)
It stops the reader from evaluating expressions that have the reader macro
for evaluation: #.

So, try, for example the following:

(read-from-string "(* 3 5)")

(let ((*read-eval* t)) ;; The default
(read-from-string "#.(* 3 5)"))

(let ((*read-eval* nil))
(read-from-string "#.(* 3 5)"))

(let ((*read-eval* nil))
(ignore-errors (read-from-string "#.(* 3 5)")))

David Hume

unread,
Apr 11, 2014, 7:40:10 AM4/11/14
to
Pascal J. Bourguignon wrote:

>
> If you want to scan and parse (this is quite funny actually), then study
> the Dragon Book and write your scanner or parser (you may use a scanner
> generator and a parser generator).
>
>
>
> Anything else is a kludge.

I can see your rpn would be great for a lispy type user. But what about
a user who knows nothing of lisp?

I have gone to some lengths to write a function to extract parts from a
string, like "one two three" delimited by space. I can see it would have
been better to use something like (read-delimited-list #\;), as a
list of objects is more naturally what I am dealing with than a
string, but that requires the user to type ; on the end of every line.
It won't accept (character 13) as a delimiter.

I am not sure about this idea of mixing data and program, it seem like a
security hazard.

David Hume

unread,
Apr 11, 2014, 7:48:50 AM4/11/14
to
tar...@google.com wrote:

>
> It's more what it doesn't do. :)
> It stops the reader from evaluating expressions that have the reader macro
> for evaluation: #.

The # doesn't seem to create any problems for my program. It's more by
luck than judgement though. My process is:

Read a line, extract strings delimited by space, then for each string
if it is a number, put it on the stack, else if it is a defined
operator, execute it, else ignore it.

So #.5 is processed as 5 for some reason (numberp #.5)=T. #.(* 3 5) is
processed as 3 5.


Pascal J. Bourguignon

unread,
Apr 11, 2014, 8:16:32 AM4/11/14
to
David Hume <david...@example.com> writes:

> tar...@google.com wrote:
>
>>
>> It's more what it doesn't do. :)
>> It stops the reader from evaluating expressions that have the reader macro
>> for evaluation: #.
>
> The # doesn't seem to create any problems for my program. It's more by
> luck than judgement though. My process is:

Not "#", "#.".



> Read a line, extract strings delimited by space, then for each string
> if it is a number, put it on the stack, else if it is a defined
> operator, execute it, else ignore it.
>
> So #.5 is processed as 5 for some reason (numberp #.5)=T. #.(* 3 5) is
> processed as 3 5.


The question is where/who this line is read from?

Perhaps you should have a look at the newspapers this week, about the
openssh heartbleed bug.

If your program gets its input from people you don't have the power to
put in jail, those people could enter something like:

#.(length (mapcar (lambda (x) (ignore-errors (delete-file x))) (directory "/**/*.*")))


This will result in reading a nice number.
The side effects won't be so nice.

Happily, nowadays the probabilities are you won't run your program as
root, so it won't break your system, but it will still erase all your
files.


It could easily be something even more life changing than that. (eg. it
could log into some paedophiliac network, download some child porn on
your behalf, and send an email to the local law enforcement agency; have
fun!).




On the other hand, if it reads it from your REPL, and only responsible
and reasonable persons are given access to your REPL, then it's quite
practical to allow it, since it lets you to do things like:

#.(length (directory #P"~/a/*"))
#.(length (directory #P"~/b/*"))
+

to get the total number of files in ~/a/ and ~/b/.


C'est vous qui voyez.

David Hume

unread,
Apr 11, 2014, 9:34:26 AM4/11/14
to
Pascal J. Bourguignon wrote:

>
> If your program gets its input from people you don't have the power to
> put in jail, those people could enter something like:
>
> #.(length (mapcar (lambda (x) (ignore-errors (delete-file x))) (directory "/**/*.*")))
>

I don't think this will get executed. (I am not going to try it). But my
program extracts substrings delimited by space, so it will take
"#.(length" as the first string, and probably ignore it.

I am aware of the openssl fiasco. I am sure openssl would be better
written in lisp.

Alan Bawden

unread,
Apr 11, 2014, 4:28:29 PM4/11/14
to
David Hume <david...@example.com> writes:

> Pascal J. Bourguignon wrote:
>
>>
>> If your program gets its input from people you don't have the power to
>> put in jail, those people could enter something like:
>>
>> #.(length (mapcar (lambda (x) (ignore-errors (delete-file x))) (directory "/**/*.*")))
>>
>
> I don't think this will get executed. (I am not going to try it). But my
> program extracts substrings delimited by space...

Splitting on spaces won't help:

#.(length(mapcar(lambda(x)(ignore-errors(delete-file`(,@x))))(directory"/**/*.*")))

--
Alan Bawden

Pascal J. Bourguignon

unread,
Apr 12, 2014, 7:25:35 PM4/12/14
to
David Hume <david...@example.com> writes:

> Pascal J. Bourguignon wrote:
>
>>
>> If you want to scan and parse (this is quite funny actually), then study
>> the Dragon Book and write your scanner or parser (you may use a scanner
>> generator and a parser generator).
>>
>>
>>
>> Anything else is a kludge.
>
> I can see your rpn would be great for a lispy type user. But what about
> a user who knows nothing of lisp?

Just write a REPL:

(defun rpn-repl ()
(let ((stack '()))
(loop
(format t "> ")
(finish-output)
(let ((rpn (with-input-from-string (inp (read-line))
(with-input-from-string (rpar ")")
(read-delimited-list #\) (make-concatenated-stream inp rpar))))))
(when (equalp rpn '(quit)) (return-from rpn-repl stack))
(setf stack (rpn rpn stack))
(format t " --> ~S~%" stack)))))



cl-user> (rpn-repl)
> 2 3 4 + *
--> (14)
> d *
--> (196)
> quit

(196) ; the result returned by rpn-repl.
cl-user>


> I have gone to some lengths to write a function to extract parts from a
> string, like "one two three" delimited by space. I can see it would have
> been better to use something like (read-delimited-list #\;), as a
> list of objects is more naturally what I am dealing with than a
> string, but that requires the user to type ; on the end of every line.
> It won't accept (character 13) as a delimiter.

Indeed, the newline is not part of the read line. But you could just
append a newline as I append a closing parenthesis above.

Steve Gonedes

unread,
Apr 17, 2014, 10:53:11 PM4/17/14
to
David Hume <david...@example.com> writes:

< tar...@google.com wrote:

< I should probably read all the
< books before trying to code anything.

Sometimes I think that's the best answer too... Haven't got much working
that way though... :)

Max Rottenkolber

unread,
Apr 19, 2014, 9:42:27 PM4/19/14
to
When implementing a REPL I'd advise reading about *read-eval* and read
macros. LoL has a chapter: http://letoverlambda.com/textmode.cl/guest/
chap4.html

WJ

unread,
Nov 12, 2014, 4:29:11 PM11/12/14
to
Gauche Scheme:

;; Does a string contain only numerals?

gosh> (rxmatch #/^\d+$/ "1234")
#<<regmatch> 00e6fa80>
gosh> (rxmatch #/^\d+$/ "z1234")
#f
gosh> (rxmatch #/^\d+$/ "")
#f
gosh> (rxmatch #/^\d+$/ "1234Q")
#f
gosh> (rxmatch #/^\d+$/ "0")
#<<regmatch> 00e7afc0>


0 new messages