From: Alex Mizrahi
Subject: simple compiler to CLI (.NET) ilasm
Date: 
Message-ID: <34j4goF4a1cvvU1@individual.net>
Hello, All!

i thought this might be interesting/useful to somebody..

i've done small/simple compiler of somthing lisp-like into .NET ilasm.
compiler generates ilasm ".il" files, that could be compiled with ilasm
utility into executable file and executed.
language is very small - it allows to define functions (and call them),
define lexical variables, use numbers as constants, add and print numbers.
integers are the only type supported, however it uses boxing/unboxing all
the time, so i think it could be easily extended to any types. (i also
believe it's quite possible to create closures with this approach).
 it's not Turing-complete, but with some extensions (i think 'if' construct
and tailcalls could be enough) it can be made so.

it's able to compile file
---
(assembly My)

(defun add (x y)
 (+ x y))

(defun-void main ()
 (entrypoint)
 (print
  (let1 (x 3)
   (let1 (y 4)
    (+ (add y x) 5)))))
---
into working executable:
---
[in clisp]
CL-USER> (load "dotNet1.lisp")
CL-USER> (il-compile-file #p"h:\\misc\\il\\my.lisp" #p"h:\\misc\\il\\my.il")
NIL

[shell]
E:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin\sdkvars.bat
H:\misc\il>ilasm my.il

Microsoft (R) .NET Framework IL Assembler.  Version 1.1.4322.2032
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
Assembling 'my.il' , no listing file, to EXE --> 'my.EXE'

Assembled global method ADD
Assembled global method MAIN
Creating PE file
...
Operation completed successfully

H:\misc\il>my.exe
12
---

here is source code:

---
;;ilasm compiler
;;Alex 'killer_storm' Mizrahi, 2005

(defun dispatch-form (form bindings)
  (if (atom form)
      (load-atom form bindings)
    (process-form (car form) (cdr form) bindings)))

(defparameter *il-commands* nil)
(defparameter *locals* nil)
(defparameter *function-signatures* nil)

(defun il-emit (&rest params)
  (setq *il-commands* (append *il-commands*
         (list (apply #'format nil params)))))

(defun load-atom (form bindings)
  (let ((binding (assoc form bindings)))
    (if binding
 (progn
   (ecase (cadr binding)
     (:arg (il-emit "ldarg ~a~%" (cddr binding)))
     (:var (il-emit "ldloc ~a~%" (cddr binding)))))
      (if (integerp form)
   (progn
     (il-emit "ldc.i4 ~a~%" form)
     (il-emit "box [mscorlib]System.Int32~%"))
 (error "unbound variable ~a" form)))))

(defparameter *ilout* nil)

(defun defun-output (fun-name param-count ret-type)
  (format *ilout* ".method public static ~A ~A(~{~A~^, ~}) cil managed~%{~%"
ret-type fun-name
   (loop repeat param-count collect "object"))
  (format *ilout* ".locals init (~{~A~^, ~})~%"
   (reverse *locals*))
  (dolist (line *il-commands*)
    (princ line *ilout*))
  (format *ilout* "}~%"))

(defun process-defun (rest bindings is-void)
  (let ((fun-name (first rest))
 (paramlist (second rest))
 (body (cddr rest)))
    (setq bindings nil)
    (setq *locals* nil)
    (setq *il-commands* nil)
    (setq *function-signatures* (acons fun-name (length paramlist)
*function-signatures*))
    (loop for i upfrom 0
      for param in paramlist
      do (setq bindings
        (acons param (cons :arg i) bindings)))
    (dolist (form body)
      (dispatch-form form bindings))
    (when is-void
      (il-emit "pop~%"))
    (il-emit "ret~%")
    (defun-output fun-name (length paramlist) (if is-void "void"
"object"))))

(defgeneric process-form (first rest bindings)
  (:method ((first (eql 'assembly)) rest bindings)
    (let ((is-extern (and (> (length rest) 1)
     (eq (second rest) :extern))))
      (format *ilout* ".assembly ~A ~A {}~%" (if is-extern "extern" "")
(first rest))))
  (:method ((first (eql 'defun)) rest bindings)
    (process-defun rest bindings nil))
  (:method ((first (eql 'defun-void)) rest bindings)
    (process-defun rest bindings t))
  (:method ((first (eql 'let1)) rest bindings)
    (let* ((declaration (first rest))
    (body (cdr rest))
    (var (first declaration))
    (init-expr (second declaration)))
      (let ((new-local-id (length *locals*)))
        (push "object" *locals*)
        (setq bindings (acons var (cons :var new-local-id) bindings))
        (dispatch-form init-expr bindings)
        (il-emit "stloc ~a~%" new-local-id)
        (dolist (form body)
   (dispatch-form form bindings)))))
  (:method ((first (eql 'entrypoint)) rest bindings)
    (il-emit ".entrypoint~%"))
  (:method ((first (eql '+)) rest bindings)
    (dispatch-form (first rest) bindings)
    (il-emit "unbox [mscorlib]System.Int32~%")
    (il-emit "ldind.i4~%")
    (dolist (form (cdr rest))
      (dispatch-form form bindings)
      (il-emit "unbox [mscorlib]System.Int32~%")
      (il-emit "ldind.i4~%")
      (il-emit "add~%"))
    (il-emit "box [mscorlib]System.Int32~%")
    ))

(defmethod process-form (first rest bindings) ;;appears to be a function
call
  (let ((fun-sig (assoc first *function-signatures*)))
    (if fun-sig
 (progn
   (when (/= (length rest) (cdr fun-sig))
     (error "parameter count mismatch for function ~a, needs ~a parameters,
got ~a"
     first (cdr fun-sig) (length rest)))
   (dolist (form rest)
     (dispatch-form form bindings))
   (il-emit "call object ~a (~{~a~^, ~})~%" first
     (loop repeat (cdr fun-sig) collect "object")) )
      (error "function ~a not found" first))))

(defmethod process-form ((first (eql 'print)) rest bindings)
    (dispatch-form (first rest) bindings)
    (il-emit "dup~%")
    (il-emit "call void [mscorlib]System.Console::WriteLine(object)"))

(defun il-compile-file (source-file-path result-file-path)
  (with-open-file (*ilout* result-file-path :direction :output :if-exists
:supersede :if-does-not-exist :create)
    (setq *function-signatures* nil)
    (with-open-file (*src* source-file-path)
      (loop for code = (read *src* nil)
 while code
 do (dispatch-form code nil)))))

---

code is not very good, but it appears to be working.

With best regards, Alex 'killer_storm' Mizrahi.

From: Ivan Boldyrev
Subject: Re: simple compiler to CLI (.NET) ilasm
Date: 
Message-ID: <aomkb2xbln.ln2@ibhome.cgitftp.uiggm.nsc.ru>
On 8987 day of my life Alex Mizrahi wrote:
> Hello, All!
>
> i thought this might be interesting/useful to somebody..
>
> i've done small/simple compiler of somthing lisp-like into .NET ilasm.
> compiler generates ilasm ".il" files, that could be compiled with ilasm
> utility into executable file and executed.

URL?

I am planning to start CL-for-.NET project.

-- 
Ivan Boldyrev

                                                  Your bytes are bitten.
From: Alex Mizrahi
Subject: Re: simple compiler to CLI (.NET) ilasm
Date: 
Message-ID: <34qsfrF4f38h0U1@individual.net>
(message (Hello 'Ivan)
(you :wrote :to '("Alex Mizrahi") :on '(Fri, 14 Jan 2005 17:24:26 +0600))
(

 ??>> i thought this might be interesting/useful to somebody..
 ??>>
 ??>> i've done small/simple compiler of somthing lisp-like into .NET ilasm.
 ??>> compiler generates ilasm ".il" files, that could be compiled with
 ??>> ilasm utility into executable file and executed.

 IB> URL?

full source was included in that message below, but if you prefer urls, here
is one:
http://indirect3d.sf.net/dotNet1.lisp
8-]

 IB> I am planning to start CL-for-.NET project.

you mean CL implementation for .NET platform?
i planned to start doing something like that too, so maybe we can cooperate
to be more efficient? ;-]

actually i don't really want to focus on doing full CL (although it may be
quite useful thing to run any standard-compilant CL application, and, for
example, integrate it in a web-page using ASP.NET technology), but do some
experiments like:

--intentional programming (source should not be a text, or at least it
should be possible to add any meta-information at any point, transparently
for user)
--intelligent IDE - it should programmer much more than text editor with a
couple of scripts, it should be able to manipulate a program, not it's
source code. for example, one of nice features it could have - source
control (like CVS) on language-level, like versions or defuns, or even their
parts. and refactoring that works on source level - if you're going to
rename some global function, it should work only on function ignoring places
where it's used as a value, and it should ignore places where it's shadowed
by flet.
--automatic type solving - wherever possible, it should automatically deduce
types from context, and at least suggest to user that types to automatically
fill type declarations.
--profile-based type solving - compiler can generate code that collects info
about types being used, then it can present that information to user to
semi-automatically fill type declarations.
--profile-based optimizations
--intelligent DSL (domain-specific language) debugging - some meta-commands
to debugger, so DSLs can be debugged at source level, or deeper, if needed.
for example, i don't really want to see stuff like

(NIL #<function SPECIAL::EVAL-PROGN 200FD5CA> ((BLOCK #:NIL (TAGBODY
#:|begin-loop-558| # # # #:|end-loop-559| #))))

in backtrace when i debug (loop for i in '(1 2 3) do (+ i nil)), unless i'm
debugging loop macro.
it get's even worse with stuff like
(let ((il '(1 . 1)) (jl '(1 1))) (loop for i in il for j in jl collect (list
i j)))

backtrace is not very informative to tell that problems are with il..
simple solution is to make something like
(WITH-DSL-CONTEXT '(FOR I IN IL)
(WHEN (OR (ENDP #:|tail-538|)) (GO #:|end-loop-535|)))
so debugger can just report contexts like - error in "for i in il" in that
"loop" when appropriate debug level is set, but maybe there's something
better..

by the way, i'd like to look at you TypeL, but lispnik.newmail.ru isn't
responding. couldn't you place it in other place?

)
(With-best-regards '(Alex Mizrahi) :aka 'killer_storm)
(prin1 "People, who lust for the Feel of keys on their fingertips (c)
Inity"))
From: Ivan Boldyrev
Subject: Re: simple compiler to CLI (.NET) ilasm
Date: 
Message-ID: <bsopb2xo31.ln2@ibhome.cgitftp.uiggm.nsc.ru>
On 8990 day of my life Alex Mizrahi wrote:
>  ??>> i've done small/simple compiler of somthing lisp-like into .NET ilasm.
>
>  IB> URL?
>
> full source was included in that message below...

Oops :)))  I haven't read rest of message attentively.

>  IB> I am planning to start CL-for-.NET project.
>
> you mean CL implementation for .NET platform?

Yep.

> i planned to start doing something like that too, so maybe we can cooperate
> to be more efficient? ;-]

Sure.  Especially because we have no language barrier :)

> actually i don't really want to focus on doing full CL (although it may be
> quite useful thing to run any standard-compilant CL application, and, for
> example, integrate it in a web-page using ASP.NET technology), but do some
> experiments like:
>
> --intentional programming
> --intelligent IDE

It is not very interesting for me.

There are two possibilities: we develop two systems that share a lot
(Lisp IL assembler which do not use ilasm, optimizer and so on); or we
develop one project but each focus on different areas.

> --automatic type solving - wherever possible, it should automatically deduce
> types from context, and at least suggest to user that types to automatically
> fill type declarations.

Yep, it is interesting for me too.  "Turtles are greatest fans of
speed" :)

Priorities:
1.  ANSI compliance.
2.  Speed.
3.  MOP.
4.  Lot of libraries :)

Perhaps, McCLIM will run someday with .NET backend :)

Yep, this is a very ambitious project. :)

> --intelligent DSL (domain-specific language) debugging - some meta-commands
> to debugger, so DSLs can be debugged at source level

Perhaps, it can be achieved with Scheme-like macro system (don't
remember exactly, which of two :)

> by the way, i'd like to look at you TypeL, but lispnik.newmail.ru isn't
> responding. couldn't you place it in other place?

Yep, Newmail.RU hosting sux, but other free hostings sux even more :(
May be, I will move it to common-lisp.net someday.

TypeL distro is small: 23Kb.  I can send you the file if you give me
appropriate mail address.  You can contact me with my e-mail in From
field.

-- 
Ivan Boldyrev

        Outlook has performed an illegal operation and will be shut down.
        If the problem persists, contact the program vendor.