;;; -*-scheme-*- ;;; Mes --- Maxwell Equations of Software ;;; Copyright © 2016,2017 Jan Nieuwenhuizen ;;; ;;; This file is part of Mes. ;;; ;;; Mes is free software; you can redistribute it and/or modify it ;;; under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 3 of the License, or (at ;;; your option) any later version. ;;; ;;; Mes is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with Mes. If not, see . ;;; Commentary: ;;; compiler.mes produces an i386 binary from the C produced by ;;; Nyacc c99. ;;; Code: (cond-expand (guile-2 (set-port-encoding! (current-output-port) "ISO-8859-1")) (guile) (mes (mes-use-module (nyacc lang c99 parser)) (mes-use-module (mes elf-util)) (mes-use-module (mes pmatch)) (mes-use-module (mes elf)) (mes-use-module (mes libc-i386)) (mes-use-module (mes optargs)))) (define (logf port string . rest) (apply format (cons* port string rest)) (force-output port) #t) (define (stderr string . rest) (apply logf (cons* (current-error-port) string rest))) (define (gnuc-xdef? name mode) (if (equal? name "__GNUC__") #f (eq? mode 'code))) (define (mescc) (parse-c99 #:inc-dirs (string-split (getenv "C_INCLUDE_PATH") #\:) #:cpp-defs '( ("__GNUC__" . "0") ("__NYACC__" . "1") ("VERSION" . "0.4") ("PREFIX" . "") ) #:xdef? gnuc-xdef? #:mode 'code )) (define (write-any x) (write-char (cond ((char? x) x) ((and (number? x) (< (+ x 256) 0)) (format (current-error-port) "***BROKEN*** x=~a\n" x) (integer->char #xaa)) ((number? x) (integer->char (if (>= x 0) x (+ x 256)))) (else (stderr "write-any: ~a\n" x) barf)))) (define (ast:function? o) (and (pair? o) (eq? (car o) 'fctn-defn))) (define (.name o) (pmatch o ((fctn-defn _ (ftn-declr (ident ,name) _) _) name) ((fctn-defn _ (ptr-declr (pointer) (ftn-declr (ident ,name) _)) _) name) ((param-decl _ (param-declr (ident ,name))) name) ((param-decl _ (param-declr (ptr-declr (pointer) (ident ,name)))) name) ((param-decl _ (param-declr (ptr-declr (pointer) (array-of (ident ,name))))) name) (_ (format (current-error-port) "SKIP .name =~a\n" o)))) (define (.statements o) (pmatch o ((fctn-defn _ (ftn-declr (ident ,name) _) (compd-stmt (block-item-list . ,statements))) statements) ((fctn-defn _ (ptr-declr (pointer) (ftn-declr (ident ,name) _)) (compd-stmt (block-item-list . ,statements))) statements))) (define ') (define ') (define ') (define ') (define ') (define ') (define* (make o #:key (functions '()) (globals '()) (locals '()) (function #f) (text '())) (pmatch o ( (list (cons functions) (cons globals) (cons locals) (cons function) (cons text))))) (define (.functions o) (pmatch o (( . ,alist) (assq-ref alist )))) (define (.globals o) (pmatch o (( . ,alist) (assq-ref alist )))) (define (.locals o) (pmatch o (( . ,alist) (assq-ref alist )))) (define (.function o) (pmatch o (( . ,alist) (assq-ref alist )))) (define (.text o) (pmatch o (( . ,alist) (assq-ref alist )))) (define (info? o) (and (pair? o) (eq? (car o) ))) (define (clone o . rest) (cond ((info? o) (let ((functions (.functions o)) (globals (.globals o)) (locals (.locals o)) (function (.function o)) (text (.text o))) (let-keywords rest #f ((functions functions) (globals globals) (locals locals) (function function) (text text)) (make #:functions functions #:globals globals #:locals locals #:function function #:text text)))))) (define (ref-local locals) (lambda (o) ;; (stderr "IDENT REF[~a]: ~a => ~a\n" o (assoc-ref locals o) (i386:ref-local (assoc-ref locals o))) (i386:ref-local (assoc-ref locals o)))) (define (ref-global globals) (lambda (o) (lambda (f g t d) (i386:ref-global (+ (data-offset o g;;lobals ) d))))) (define (expr->arg globals locals) ;; FIXME: get Mes curried-definitions (lambda (o) (pmatch o ((p-expr (fixed ,value)) (string->number value)) ((p-expr (string ,string)) ((ref-global globals) string)) ((p-expr (ident ,name)) ((ref-local locals) name)) ((array-ref (p-expr (fixed ,value)) (p-expr (ident ,name))) (let ((value (string->number value)) (size 4)) ;; FIXME: type: int (lambda (f g t d) (append ((ident->base locals) name) (i386:value->accu (* size value)) ;; FIXME: type: int (i386:mem->accu) ;; FIXME: type: int (i386:push-accu) ;; hmm )))) (_ (format (current-error-port) "SKIP expr->arg=~a\n" o) 0)))) (define (ident->accu locals) (lambda (o) (i386:local->accu (assoc-ref locals o)))) (define (accu->ident locals) (lambda (o) (i386:accu->local (assoc-ref locals o)))) (define (ident->base locals) (lambda (o) (i386:local->base (assoc-ref locals o)))) (define (expr->accu info) (lambda (o) (pmatch o ((p-expr (fixed ,value)) (string->number value)) ((p-expr (ident ,name)) ((ident->accu (.locals info)) name)) ((fctn-call . _) ((ast->info info) `(expr-stmt ,o))) ((sub . _) ((ast->info info) o)) ;; FIXME: expr-stmt (_ (format (current-error-port) "SKIP expr->accu=~a\n" o) 0) ))) (define (string->global string) (cons string (append (string->list string) (list #\nul)))) (define (expr->global o) (pmatch o ((p-expr (string ,string)) (string->global string)) (_ #f))) (define (dec->hex o) (number->string o 16)) (define (byte->hex o) (string->number (string-drop o 2) 16)) (define (asm->hex o) (let ((prefix ".byte ")) (if (not (string-prefix? prefix o)) (begin (stderr "SKIP:~a\n" o)'()) (let ((s (string-drop o (string-length prefix)))) (map byte->hex (string-split s #\space)))))) (define (ast->info info) (lambda (o) (let ((globals (.globals info)) (locals (.locals info)) (text (.text info))) (define (add-local name) (acons name (1+ (or (and=> (member 1 (map cdr locals)) length) 0)) locals)) ;; (stderr "\nS=~a\n" o) ;; (stderr " text=~a\n" text) ;; (stderr " info=~a\n" info) ;; (stderr " globals=~a\n" globals) (pmatch o (((trans-unit . _) . _) ((ast-list->info info) o)) ((trans-unit . ,elements) ((ast-list->info info) elements)) ((fctn-defn . _) ((function->info info) o)) ((comment . _) info) ((cpp-stmt (define (name ,name) (repl ,value))) (stderr "SKIP: #define ~s ~s\n" name value) info) ((compd-stmt (block-item-list . ,statements)) ((ast-list->info info) statements)) ((expr-stmt (fctn-call (p-expr (ident ,name)) (expr-list . ,expr-list))) (if (equal? name "asm") (let ((arg0 (cadr (cadar expr-list)))) ;; FIXME (clone info #:text (append text (list (lambda (f g t d) (asm->hex arg0)))))) (let* ((globals (append globals (filter-map expr->global expr-list))) (args (map (expr->arg globals locals) expr-list))) (clone info #:text (append text (list (lambda (f g t d) (apply i386:call (cons* f g t d (+ t (function-offset name f)) args))))) #:globals globals)))) ((if ,test ,body) (let* ((jump (pmatch test ((lt ,a ,b) i386:jump-nc) ((gt ,a ,b) i386:jump-nc) (_ i386:jump-z))) (jump-text (lambda (body-length) (list (lambda (f g t d) (jump body-length))))) (test-info ((ast->info info) test)) (test+jump-info (clone test-info #:text (append (.text test-info) (jump-text 0)))) (text-length (length (.text test+jump-info))) (body-info ((ast->info test+jump-info) body)) (body-text (list-tail (.text body-info) text-length)) (body-length (length (text->list body-text)))) (clone info #:text (append (.text test-info) (jump-text body-length) body-text) #:globals (.globals body-info)))) ((for ,init ,test ,step ,body) (let* ((jump (pmatch test ((lt ,a ,b) i386:jump-c) ((gt ,a ,b) i386:jump-c) (_ i386:jump-nz))) (jump-text (lambda (body-length) (list (lambda (f g t d) (jump body-length))))) (info (clone info #:text '())) (info ((ast->info info) init)) (init-text (.text info)) (init-locals (.locals info)) (info (clone info #:text '())) (body-info ((ast->info info) body)) (body-text (.text body-info)) (body-length (length (text->list body-text))) (step-info ((ast->info info) `(expr-stmt ,step))) (step-text (.text step-info)) (step-length (length (text->list step-text))) (test-info ((ast->info info) test)) (test-text (.text test-info)) (test-length (length (text->list test-text)))) (clone info #:text (append text init-text (list (lambda (f g t d) (i386:jump (+ 2 body-length)))) ;; FIXME: 2 body-text step-text test-text (jump-text (- (+ body-length step-length test-length)))) #:globals (append globals (.globals body-info)) ;; FIXME #:locals locals))) ((while ,test ,body) (let* ((jump (pmatch test ((lt ,a ,b) i386:jump-c) ((gt ,a ,b) i386:jump-c) ;;(_ i386:jump-nz) (_ i386:jump-byte-nz) ;; FIXME )) (jump-text (lambda (body-length) (list (lambda (f g t d) (jump body-length))))) (info (clone info #:text '())) (body-info ((ast->info info) body)) (body-text (.text body-info)) (body-length (length (text->list body-text))) (test-info ((ast->info info) test)) (test-text (.text test-info)) (test-length (length (text->list test-text)))) (clone info #:text (append text (list (lambda (f g t d) (i386:jump (+ 2 body-length)))) ;; FIXME: 2 body-text test-text (jump-text (- (+ body-length test-length)))) #:globals (.globals body-info)))) ((labeled-stmt (ident ,label) ,statement) (let ((info (clone info #:text (append text (list label))))) ((ast->info info) statement))) ((goto (ident ,label)) (let ((offset (length (text->list text)))) (clone info #:text (append text (list (lambda (f g t d) (i386:jump (- (label-offset (.function info) label f) offset)))))))) ((p-expr (ident ,name)) (clone info #:text (append text (list (lambda (f g t d) (append (i386:local->accu (assoc-ref locals name)) (i386:accu-zero?))))))) ((p-expr (fixed ,value)) (let ((value (string->number value))) (clone info #:text (append text (list (lambda (f g t d) (append (i386:value->accu value) (i386:accu-zero?)))))))) ((de-ref (p-expr (ident ,name))) (clone info #:text (append text (list (lambda (f g t d) (append (i386:local->accu (assoc-ref locals name)) (i386:byte-mem->accu))))))) ((fctn-call . ,call) (let ((info ((ast->info info) `(expr-stmt ,o)))) (clone info #:text (append (.text info) (list (lambda (f g t d) (i386:accu-zero?))))))) ;; i++ ((expr-stmt (post-inc (p-expr (ident ,name)))) (clone info #:text (append text (list (lambda (f g t d) (i386:local-add (assoc-ref locals name) 1)))))) ;; ++i -- same for now FIXME ((expr-stmt (pre-inc (p-expr (ident ,name)))) (clone info #:text (append text (list (lambda (f g t d) (i386:local-add (assoc-ref locals name) 1)))))) ((not ,expr) (let* ((test-info ((ast->info info) expr))) (clone info #:text (append (.text test-info) (list (lambda (f g t d) (i386:xor-zf)))) #:globals (.globals test-info)))) ((and ,a ,b) (let* ((info (clone info #:text '())) (a-info ((ast->info info) a)) (a-text (.text a-info)) (a-length (length (text->list a-text))) (b-info ((ast->info info) b)) (b-text (.text b-info)) (b-length (length (text->list b-text)))) (clone info #:text (append text a-text (list (lambda (f g t d) (i386:jump-byte-z (+ b-length 2)))) ;; FIXME: need jump after last test b-text)))) ;; FIXME and, gt ((eq (de-ref (p-expr (ident ,a))) (de-ref (p-expr (ident ,b)))) (clone info #:text (append text (list (lambda (f g t d) (append (append (i386:local->accu (assoc-ref locals a)) (i386:byte-mem->base) (i386:local->accu (assoc-ref locals b)) (i386:byte-mem->accu) (i386:byte-test-base)))))))) ((gt (p-expr (ident ,a)) (p-expr (fixed ,b))) ;; (stderr "GT: ~a > ~a\n" a b) (let ((b (string->number b))) (clone info #:text (append text (list (lambda (f g t d) (append (i386:local->base (assoc-ref locals a)) (i386:value->accu b) (i386:sub-base)))))))) ((eq (p-expr (ident ,a)) (p-expr (fixed ,b))) ;;(stderr "EQ: ~a > ~a\n" a b) (let ((b (string->number b))) (clone info #:text (append text (list (lambda (f g t d) (append (i386:local->base (assoc-ref locals a)) (i386:value->accu b) (i386:sub-base) (i386:xor-zf)))))))) ((ne (p-expr (ident ,a)) (p-expr (fixed ,b))) ;;(stderr "NE: ~a > ~a\n" a b) (let ((b (string->number b))) (clone info #:text (append text (list (lambda (f g t d) (append (i386:local->base (assoc-ref locals a)) (i386:value->accu b) (i386:sub-base)))))))) ((lt (p-expr (ident ,a)) (p-expr (fixed ,b))) ;;(stderr "LT: ~a < ~a\n" a b) (let ((b (string->number b))) (clone info #:text (append text (list (lambda (f g t d) (append (i386:local->base (assoc-ref locals a)) (i386:value->accu b) (i386:base-sub)))))))) ((sub (de-ref (p-expr (ident ,a))) (de-ref (p-expr (ident ,b)))) (clone info #:text (append text (list (lambda (f g t d) (append (i386:local->accu (assoc-ref locals a)) (i386:byte-mem->base) (i386:local->accu (assoc-ref locals b)) (i386:byte-mem->accu) (i386:byte-sub-base))))))) ((array-ref (p-expr (fixed ,value)) (p-expr (ident ,name))) (let ((value (string->number value))) (clone info #:text (append text (list (lambda (f g t d) (append ((ident->base locals) name) (i386:value->accu value) (i386:byte-mem->accu)))))))) ; FIXME: type: char ((array-ref (p-expr (ident ,name)) (p-expr (ident ,index))) (clone info #:text (append text (list (lambda (f g t d) (append ((ident->base locals) name) ((ident->accu locals) index) (i386:byte-mem->accu))))))) ; FIXME: type: char ((return ,expr) (let ((accu ((expr->accu info) expr))) (if (info? accu) (clone accu #:text (append (.text accu) (list (i386:ret (lambda _ '()))))) (clone info #:text (append text (list (i386:ret ((expr->accu info) expr)))))))) ;; int i; ((decl (decl-spec-list (type-spec (fixed-type ,type))) (init-declr-list (init-declr (ident ,name)))) (clone info #:locals (add-local name))) ((decl (decl-spec-list (type-spec (fixed-type ,type))) (init-declr-list (init-declr (ident ,name) (initzer (p-expr (fixed ,value)))))) (let ((locals (add-local name))) (let ((value (string->number value))) (clone info #:text (append text (list (lambda (f g t d) (i386:local-assign (assoc-ref locals name) value)))) #:locals locals)))) ;; int i = argc; ((decl (decl-spec-list (type-spec (fixed-type ,type))) (init-declr-list (init-declr (ident ,name) (initzer (p-expr (ident ,local)))))) (let ((locals (add-local name))) (clone info #:text (append text (list (lambda (f g t d) (append ((ident->accu locals) local) ((accu->ident locals) name))))) #:locals locals))) ;; SCM i = argc; ((decl (decl-spec-list (type-spec (typename ,type))) (init-declr-list (init-declr (ident ,name) (initzer (p-expr (ident ,local)))))) (let ((locals (add-local name))) (clone info #:text (append text (list (lambda (f g t d) (append ((ident->accu locals) local) ((accu->ident locals) name))))) #:locals locals))) ;; int i = f (); ((decl (decl-spec-list (type-spec (fixed-type ,type))) (init-declr-list (init-declr (ident ,name) (initzer (fctn-call . ,call))))) (let* ((locals (add-local name)) (info (clone info #:locals locals))) (let ((info ((ast->info info) `(expr-stmt (fctn-call ,@call))))) (clone info #:text (append (.text info) (list (lambda (f g t d) (i386:ret-local (assoc-ref locals name))))) #:locals locals)))) ;; i = 0; ((expr-stmt (assn-expr (p-expr (ident ,name)) (op _) (p-expr (fixed ,value)))) ;;(stderr "RET LOCAL[~a]: ~a\n" name (assoc-ref locals name)) (let ((value (string->number value))) (clone info #:text (append text (list (lambda (f g t d) (i386:local-assign (assoc-ref locals name) value))))))) ;; i = 0; ...from for init FIXME ((assn-expr (p-expr (ident ,name)) (op _) (p-expr (fixed ,value))) (let ((value (string->number value))) (clone info #:text (append text (list (lambda (f g t d) (i386:local-assign (assoc-ref locals name) value))))))) ((expr-stmt (assn-expr (p-expr (ident ,name)) (op _) (fctn-call . ,call))) (let* ((info ((ast->info info) `(expr-stmt (fctn-call ,@call))))) (clone info #:text (append (.text info) (list (lambda (f g t d) (i386:ret-local (assoc-ref locals name)))))))) (_ (format (current-error-port) "SKIP statement=~s\n" o) info))))) (define (info->exe info) (display "dumping elf\n" (current-error-port)) (map write-any (make-elf (.functions info) (.globals info)))) (define (.formals o) (pmatch o ((fctn-defn _ (ftn-declr _ ,formals) _) formals) ((fctn-defn _ (ptr-declr (pointer) (ftn-declr _ ,formals)) _) formals) (_ (format (current-error-port) ".formals: no match: ~a\n" o) barf))) (define (formal->text n) (lambda (o i) ;;(i386:formal i n) '() )) (define (formals->text o) (pmatch o ((param-list . ,formals) (let ((n (length formals))) (list (lambda (f g t d) (append (i386:function-preamble) (append-map (formal->text n) formals (iota n)) (i386:function-locals)))))) (_ (format (current-error-port) "formals->text: no match: ~a\n" o) barf))) (define (formals->locals o) (pmatch o ((param-list . ,formals) (let ((n (length formals))) ;;(stderr "FORMALS: ~a ==> ~a\n" formals n) (map cons (map .name formals) (iota n -2 -1)))) (_ (format (current-error-port) "formals->info: no match: ~a\n" o) barf))) (define (function->info info) (lambda (o) ;;(stderr "\n") ;;(stderr "formals=~a\n" (.formals o)) (let* ((name (.name o)) (text (formals->text (.formals o))) (locals (formals->locals (.formals o)))) (format (current-error-port) "compiling ~a\n" name) ;;(stderr "locals=~a\n" locals) (let loop ((statements (.statements o)) (info (clone info #:locals locals #:function name #:text text))) (if (null? statements) (clone info #:function #f #:functions (append (.functions info) (list (cons (.name o) (.text info))))) (let* ((statement (car statements))) (loop (cdr statements) ((ast->info info) (car statements))))))))) (define (ast-list->info info) (lambda (elements) (let loop ((elements elements) (info info)) (if (null? elements) info (loop (cdr elements) ((ast->info info) (car elements))))))) (define _start (let* ((argc-argv (string-append ".byte" " 0x89 0xe8" ; mov %ebp,%eax " 0x83 0xc0 0x08" ; add $0x8,%eax " 0x50" ; push %eax " 0x89 0xe8" ; mov %ebp,%eax " 0x83 0xc0 0x04" ; add $0x4,%eax " 0x0f 0xb6 0x00" ; movzbl (%eax),%eax " 0x50" ; push %eax )) (ast (with-input-from-string (string-append "int _start () {int i;asm(\"" argc-argv "\");i=main ();exit (i);}") parse-c99))) ast)) (define strlen (let* ((ast (with-input-from-string " int strlen (char const* s) { int i = 0; while (s[i]) i++; return i; } " ;;paredit:" parse-c99))) ast)) (define eputs (let* ((ast (with-input-from-string " int eputs (char const* s) { //write (STDERR, s, strlen (s)); //write (2, s, strlen (s)); int i = strlen (s); write (2, s, i); return 0; } " ;;paredit:" parse-c99))) ast)) (define fputs (let* ((ast (with-input-from-string " int fputs (char const* s, int fd) { int i = strlen (s); write (fd, s, i); return 0; } " ;;paredit:" parse-c99))) ast)) (define puts (let* ((ast (with-input-from-string " int puts (char const* s) { //write (STDOUT, s, strlen (s)); //int i = write (STDOUT, s, strlen (s)); int i = strlen (s); write (1, s, i); return 0; } " ;;paredit:" parse-c99))) ast)) (define strcmp (let* ((ast (with-input-from-string " int strcmp (char const* a, char const* b) { while (*a && *b && *a == *b) { a++;b++; } return *a - *b; } " ;;paredit:" parse-c99))) ast)) (define i386:libc (list (cons "exit" (list i386:exit)) (cons "write" (list i386:write)))) (define libc (list strlen eputs fputs puts strcmp)) (define (compile) (let* ((ast (mescc)) (info (make #:functions i386:libc)) (info ((ast->info info) libc)) (info ((ast->info info) ast)) (info ((ast->info info) _start))) (info->exe info)))