This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

Mapping a different syntax to Lisp

11 posts · 5113 views

It would be great to impletemt a simple language that is more natural looking to people whos heads explode when they see lots of parenthesis, but which maps onto Lisp. Here is an infix interpreter. Feel free to tell me what can be better.

The idea is that you can put in a string, say "1 + 2 - 6", have the interpreter translate it to lisp. All operators have same precidence, left to right. You can also force certain parts to be evaluated together, via "1 + (6 / 3)"

Here is code for the infix interpreter.
(defun infix-interpreter (string)
	   (eval 
	    (infix-to-postfix
	     (with-input-from-string (stream string)
	       (let (elts)
		 (labels ((read-collect ()
			    (let ((elt (read stream nil nil)))
			      (if elt (push elt elts)))))
		   (while (read-collect))
		   (reverse elts)))))))
	 

(defun infix-to-postfix (list)
	   (labels ((transform (list)
		      (if (or (atom (car list))
			      (not (equal (caar list) (second list))))
			  (list (second list) (first list) (third list))
			  (append (first list) (cddr list))))
		    (element-transform (elt)
			(if (listp elt)
			    (expression elt)
			    elt))
		    (expression (list)
		      (cond ((>= (length list) 3)
				 (multiple-value-bind (exp rest) (split-list list 3)
				       (let ((trans (transform (list 
								(first exp) 
								(second exp)
								(element-transform (third exp))))))
					 (if rest
					     (expression (cons trans rest))
					     trans))))
			    ((= (length list) 1)
			     (car list))
			    (t
			     (error (string-concat "Expression ends with \"" (write-to-string (second list)) "\" as operator."))))))
	     (expression (cons (element-transform (car list)) (cdr list)))))

(defun split-list (list index)
	   (cond ((and list (> index 0))
		  (multiple-value-bind (before-list after-list)
		      (split-list (cdr list) (1- index))
		    (values (cons (car list) before-list)
			    after-list)))
		 ((and list (= index 0))
		  (multiple-value-bind (before-list after-list)
		      (split-list (cdr list) index)
		    (values before-list
			    (cons (car list) after-list))))
		 (t
		  (values nil nil))))

(defmacro while (test &rest body)
  `(do ()
       ((not ,test))
     ,@body))
So I am musing over how to implement functions, or define local variables. Maybe have another interpreter on top of this one that toggles between say function-call-mode and infix-mode. But what would look good? What could signal that this is a function, say "function(arg-a arg-b)" and "1 + 2 + 3". Maybe the pound sign.

Maybe then, for a local var, you could do #let(x 26) {1 + x} becomes (let ((x 26)) (+ 1 x))). The call could embrace the rest of the file. So #let(x 22) without the brackets could translate to (let ((x 22))
.....rest of file here.....
)
Comma separators would allow infix notation within the function call argument list. #/ (1 + 2, 5 - 6 + 3, f)

So let could also be #let(a, 15 + x)

Anyway, seems like it would be cool for user configuration files.

Any feedback, from code critique, to snippit sketches, to ideas about the structure of how things can work, to anything that gets us thinking would be awsome.

Re: Mapping a different syntax to Lisp

Turning Lisp into line-noise isn't going to help anyone who can't cope with parentheses. If you're going to do this, do it right: make a reader for something very like Python syntax, with proper operator precedence, etc.

Re: Mapping a different syntax to Lisp

I want to make something compact but easier on the eyes for someone who doen't like the parenthesis. And Im not doing it wrong because it doent look like Python, or because it has a regular precidence. But thanks for sharing your thoughts anyway.

Re: Mapping a different syntax to Lisp

Your enthusiasm is commendable, but the quiet reception is because this is a recurrent meme: newcomers regularly try to "fix" what they see as a shortcoming in the language. Unfortunately, it ultimately makes about as much sense as "fixing" C by grafting on a prefix notation layer to make it more familiar for hard-core lispers.
Things generally seem to go one way or another: either the parentheses make sense and stop bothering the programmer after a while, or they can't get past the surface appearance and eventually give up and go back to the Algol family. John McCarthy himself planned early on to add this kind of thing on top of the core language, but in the last 50 years nobody's quite gotten around to it.

It might help to spend a while wrapping your head around the idea that they're just markers for the beginning and end of (often nested) lists - what you're manipulating in Lisp source-code is the equivalent of the AST that is produced after C-style source-code has been transformed into something convenient for the compiler. I'm not being sarcastic; this is a non-trivial mindshift for many, that you're working one step closer to the machine than usual. Personally, I love the disambiguation they afford: no more arbitrary precedence rules to memorise or to trip you up.

All that said, by all means go ahead and see what you can accomplish in this direction, possibly after looking around at what your predecessors have come up with - it may save you time, show you a more promising way to do it, or affirm that you're heading in the right direction. At the least, it'll be one way of getting to grips with the language and how it works. Speaking as somebody who's solved a similar problem, you might want to first work on your preferred problem (the infix notation layer), and then go back and worry about on-the-fly autodetection. I've just pulled autodetection out of mark-and-render because I saw what kind of a massive detour it was leading to, and that's just on a file-by-file level.
Also, if I understand correctly, reader macros are what you need to mess with here.


HTH,
James

Re: Mapping a different syntax to Lisp

Reposting with code in the code brackets.

Thank you for the insightfull and thoughtfull reply. I certainly am not changing the language to make it any better for me, or a Lisper to use. For now, the idea is to make a mapping that is easier to understand for someone who doesn't care about Lisp. If they wanted more power, then a user could be given a subset of the language. But in this case, for say, a simple configuration file, a non Lisper looks at this
(let ((x 15))
   (setf y (+ x 13)))
and think, WTF is that? Is this guy crazy? What does he expect of me?

I think
#let (x, 15) {
     #setf (y, 13 + x)}
would be much easier on the eyes to them. I think because there are more key symbols, everything seems more compartmentalized.
Further, x = y could map to (setf x y) and maybe (x == y) could map to (= x y)

So the above could be
#let (x, 15){
    y = (13 + x)}
or even
#let (x, 15)
    y = (13 + x)
Thats easier for me to look at then Lisp. And I would personally want to program in Lisp a million times more then this concotion. Its about impressions. I want a person to look in their configuration and see something that looks like, hey, I can mess with that.

Its also about the tools. When you Lisp you need to have an editor that supports it. I can barely type lisp code without one, forget about making a program.

Also, I think I will have to mess with the reader to get the pound sign to read in.

Once again, thank you for the reply.

Re: Mapping a different syntax to Lisp

Below I give concepts that I think can be used to develop the lisp infix interpreter. Maybe its wrong. Maybe there is that "oooohhhhh, thats why they dont do it like that" right around the corner. If you can grapple with the concepts then please let me know what you think.

I had an interesting insight.

Technically, postfix is a common attribute of every language I have ever used. A common form type is:
function(arg-1, arg-2)
This is postfix in the sense that it is simply another way of writting:
(function arg-1 arg-2)
The insight is that I can toggle bettween postfix and infix dependent on if the operator is a list or not.
a + b + c
takes the pattern: element, operator, element, ......
So if the operator part of the pattern is a list, as in
a (x) 
then interpret 'a' as a function instead of an operand, and treat the list as the arguments.

I could still keep the forced precidence:
a + (b + c)
because the list is in the element part of the pattern, the program could distinguish bettween them.

Further, I would keep the comma separated arguments for functions so each argument could be infix.
fun(a + 1, b + 2, c + (x / 4))
Each infix function would have to be able to take two arguments. Here are some examples of uses using common lisp functions
print ("hello")
print (string-concat ("hello", " world"))
'a cons list('b, 'c, 'd)
Further the code could look even neater using the following convention.
a(b) becomes (a b)
a(b)(c) has two stages (b)(c) becomes ((b) c) and a ((b) c) becomes (a (b) c)
So, for example:
(dolist (a 15)
  (print a))
Could be written
dolist (a, 15)
 (
   print (a)
 )
But I seem to need a way to represent literal lists.
(let ((x 15)(y 8))
   (+ x y))
I would like to be able to preserve the structure of the ((x 15)(y 8)) part of the form using syntax so I dont have to handcode for let, or every form that has highly irregular behavior. OK. New roadblock, but good progress.

Maybe use square brackets for literal lists.
let [[x, 15],[y, 8]]
  (
    + (x , y)
  )
I know it looks like a kludge, but at least you are not forced to handcode `let', or any of the other forms that require literal lists, until you get to it. Further, edits to `let' can be reverse compatable. I think it would be better to make the lists literal by default for the firs argument of `let'.
 let ((x 15) (y 33)) 
   ( 
    x + y
   ) 
Just dont use the square brackets in the pretty version (why would you?) and the interpreter can distinguish.

Last basic element I can think of now is delimiting statements so an expression doent run on and on. These could be `,' and, and the mechanism might be the same as the commas use in the argument lists, in which each argument is really a statement.

I think that about covers the very very very basics. Any loopholes? Did I miss something?

Last edited by adam33147 on , edited 6 times in total.

Re: Mapping a different syntax to Lisp

To give you more of an idea of the flavor,
To do this.
(mapcar 
  (lambda (x y)
	string-concat x y))
  (list "a" "b" "c")
  (list "x" "y" "z"))
you could use
mapcar (
   lambda (x, y) 
      (string-concat (x, y))
    list ("a", "b", "c"),
    list ("x", "y", "z"))
or
mapcar (
  lambda (x, y) 
       (x string-concat y)
   list ("a", "b", "c"),
   list ("x", "y", "z"))
Notice how in the second version, the string-concat acts as an infix operator.

I much prefer the lisp syntax, but with the infix syntax, you can have code that looks like this
user-greeting = string-concat ("Welcome ", user-name, "!")
as opposed to this:
(setf user-greeting (string-concat "Welcome " user-name "!"))
By mapping the `=' symbol to the `setf' symbol. Not that I prefer that. But 99% of the users will appreciate that you can do this.
And the other 1% will appreciate that they can actually use Common Lisp.

Last edited by adam33147 on , edited 1 time in total.

Re: Mapping a different syntax to Lisp

Its difficult to describe how it would work because its subtle. But another way to coneptualize it, is if you are in the "postfix mode" (the toggling of which i explained above), the opening bracket of the right most list moves to the left till it reaches the first atom.
defun (a, b) (x, y, z)
would cause the intepreter to recusively (cons 'defun (cons '(a b) '( x y z)))
outputing
(defun (a b) x y z))

Interesting to note that it looks so similar to other more popular languages on many levels, not just the obvious. I learned alot working on this design. Hope others can gleen insight into the nature of programming from this also. Look forward to implementing it.

Re: Mapping a different syntax to Lisp

It appears that you are reinventing something like Sweet-Expressions.

If you're looking for a good syntax, take a look at Scribble. People won't look unless your syntax adds something. Traditional lisp syntax is not convenient for quoting non-lisp syntax (call this "strings"), and Scribble fixes that. It makes for a good templating system that might pave the way for new levels of expressiveness.

On a related note, I'd seriously be interested in a lispy syntax for C++. I've dabbled with a few things, but haven't yet found something that is visually pleasing. Why bother? Because CPP macros don't know syntax, C++ templates can't manipulate strings or token names, and a few other goodies in the back of my mind. CLang's AST, like Scheme syntax objects, isn't exactly editable. (Note: C++ started as the cfront preprocessor...)

Re: Mapping a different syntax to Lisp

Thank you for the feedback. Sweet-expressions looks really interesting. I think that after implementing this, I can give that a serious look. (after all, one of the reasons I like lisp is that I like to learn first hand by implenting things). The way their expressions look are so much better then what I am working on implementing.
define fibfast(n) ;
  if {n < 2} ; 
    n; 
    fibup(n 2 1 0) ;
Looks way better then what I am trying to implement
defun [fibfast] (n)
  (if  (n < 2, 
         n, 
         fibup(n, 2, 1, 0))),
The reason that the function name is in the [] is because the odd element position will determine if it is an infix or postfix function call.
+(a, 1),  -> (+ a 1) -> postfix call with list in odd position
a + 1,     -> (+ a 1) -> infix call with symbol in odd position
or [x] (y) -> (or x y) -> postfix call with bracked symbol 'x in odd position -> (cons 'or (cons 'x (cons '(y)))
defun [name] (a, b, c) 
  (do-something (a, b, c)), -> postfix call with bracketd symbol 'name in odd position -> (cons 'defun (cons 'name (cons '(a b c) (cons .....)
Scribble seems like something that I might personally like to use. At a glance it seems like a way of intregrating strings, commands, and data using a meta syntactic structure. It is beyond me to understand it at a glance though, I would have to study it.

The following quote from Yukihiro Matsumoto steered me clear from C++. http://www.artima.com/intv/rubyP.html
"I was a C++ programmer before I started designing Ruby. I programmed in C++ exclusively for two or three years. And after two years of C++ programming, it still surprised me."

I guess C++ is a tough language. Maybe the ridgidity of the AST is the cause of this symptom?

Closest I got to C++ was Java, and Im glad to be done with it. Forced OOP caused be to walk around with a binder full of peices of paper with boxes and arrows. If I wanted to change the way my main project functioned in any kind of serious way, maybe refactoring to better interfaces, it ment about 3 months of coding and testing. What a painfull way to play with program design.

Thinking in Lisp is so much more natural. I have already done things that would have taken me lifetimes in Java.

These compartmentalization languages have a place in large organizations, but they are impossable to fly solo with. This is not TRUTH and GOSPEL, but its my rule of thumb.

Re: Mapping a different syntax to Lisp

This is turning out to be alot tougher then I thought. I want to make the syntax better. I have the concepts, but implemeting them is really hard.
For example, the following
defun plus (a,b)
 {a + b}

defun minus (a,b)
 {a - b}
Could be implemented if the {} are interpreted to mean a list with the opening bracket that snaps back to a pair of newlines (this would also end the expression, so the user doesn't have to put the comma after). My latest insight is to use a tagging mechanism that tags the elements of the parsed tree ..ie..conses a var with value (gensym "SNAP-TO-NEWLINE") on the beggining of a curly bracketed list for example, or the sets newline and return marcocharacters to be a var with value (gensym "NEWLINE") for another example. These can be filtered out at before evaluation, but could be keys for the intepreter.

I need to a way to deal with trees on this level. I think that the tagged element tree manipulation program is the program I need to write before I can write the syntax mapping. But my thinking on what this program would be is very fuzzy. I guess it would be like a program that would allow you to implement an interpreter, then you could use it to implement your interpreter. Then I could use it to implement this interpreter. I think it certainly seems usefull. I is a good exercise to build tool that allows you to approach a problem in a new way.