Mapping a different syntax to Lisp

Discussion of Common Lisp
adam33147
Posts: 20
Joined: Sat Aug 20, 2011 6:49 pm

Mapping a different syntax to Lisp

Post by adam33147 » Tue Aug 30, 2011 5:09 pm

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.

Code: Select all

(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.

Paul
Posts: 106
Joined: Tue Jun 02, 2009 6:00 am

Re: Mapping a different syntax to Lisp

Post by Paul » Tue Aug 30, 2011 6:07 pm

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.

adam33147
Posts: 20
Joined: Sat Aug 20, 2011 6:49 pm

Re: Mapping a different syntax to Lisp

Post by adam33147 » Tue Aug 30, 2011 6:16 pm

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.

JamesF
Posts: 98
Joined: Thu Jul 10, 2008 7:14 pm

Re: Mapping a different syntax to Lisp

Post by JamesF » Wed Aug 31, 2011 1:17 am

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

adam33147
Posts: 20
Joined: Sat Aug 20, 2011 6:49 pm

Re: Mapping a different syntax to Lisp

Post by adam33147 » Wed Aug 31, 2011 4:49 am

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

Code: Select all

(let ((x 15))
   (setf y (+ x 13)))
and think, WTF is that? Is this guy crazy? What does he expect of me?

I think

Code: Select all

#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

Code: Select all

#let (x, 15){
    y = (13 + x)}
or even

Code: Select all

#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.

adam33147
Posts: 20
Joined: Sat Aug 20, 2011 6:49 pm

Re: Mapping a different syntax to Lisp

Post by adam33147 » Wed Aug 31, 2011 5:03 pm

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:

Code: Select all

function(arg-1, arg-2)
This is postfix in the sense that it is simply another way of writting:

Code: Select all

(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.

Code: Select all

a + b + c
takes the pattern: element, operator, element, ......
So if the operator part of the pattern is a list, as in

Code: Select all

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:

Code: Select all

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.

Code: Select all

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

Code: Select all

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:

Code: Select all

(dolist (a 15)
  (print a))
Could be written

Code: Select all

dolist (a, 15)
 (
   print (a)
 )
But I seem to need a way to represent literal lists.

Code: Select all

(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.

Code: Select all

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'.

Code: Select all

 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 Thu Sep 01, 2011 4:44 pm, edited 6 times in total.

adam33147
Posts: 20
Joined: Sat Aug 20, 2011 6:49 pm

Re: Mapping a different syntax to Lisp

Post by adam33147 » Wed Aug 31, 2011 6:41 pm

To give you more of an idea of the flavor,
To do this.

Code: Select all

(mapcar 
  (lambda (x y)
	string-concat x y))
  (list "a" "b" "c")
  (list "x" "y" "z"))
you could use

Code: Select all

mapcar (
   lambda (x, y) 
      (string-concat (x, y))
    list ("a", "b", "c"),
    list ("x", "y", "z"))
or

Code: Select all

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

Code: Select all

user-greeting = string-concat ("Welcome ", user-name, "!")
as opposed to this:

Code: Select all

(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 Thu Sep 01, 2011 4:40 am, edited 1 time in total.

adam33147
Posts: 20
Joined: Sat Aug 20, 2011 6:49 pm

Re: Mapping a different syntax to Lisp

Post by adam33147 » Wed Aug 31, 2011 7:21 pm

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.

nuntius
Posts: 538
Joined: Sat Aug 09, 2008 10:44 am
Location: Newton, MA

Re: Mapping a different syntax to Lisp

Post by nuntius » Fri Sep 02, 2011 9:47 pm

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...)

adam33147
Posts: 20
Joined: Sat Aug 20, 2011 6:49 pm

Re: Mapping a different syntax to Lisp

Post by adam33147 » Sat Sep 03, 2011 2:05 pm

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.

Code: Select all

define fibfast(n) ;
  if {n < 2} ; 
    n; 
    fibup(n 2 1 0) ;
Looks way better then what I am trying to implement

Code: Select all

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.

Code: Select all

+(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.

Post Reply