Login
7 branches 0 tags
Ben (X13/Arch) Renamed [%] -> [rem] 6c19bd3 3 years ago 712 Commits
nujel / stdlib / compiler / backend_bytecode / bytecompile.nuj
;; Nujel - Copyright (C) 2020-2021 - Benjamin Vincent Schulenburg
;; This project uses the MIT license, a copy should be included under /LICENSE
;;
;; Contains the Bytecode compiler that takes macroexpanded Nujel code and emits
;; Bytecode assembly
[def bytecompile/gen-label/counter 0]
[defn bytecompile/gen-label [prefix]
      [++ bytecompile/gen-label/counter]
      [string->symbol [cat prefix "label-" bytecompile/gen-label/counter]]]

[def *bytecompile-push* #f]
[defn $push/v [v]
      [if *bytecompile-push*
          [$push/val v]
          [$push/lval v]]]

[defn bytecompile/literal [source]
      [case [type-of source]
            [[:symbol :keyword] [if [keyword? source]
                                    [$push/v source]
                                    [$get source]]]
            [:int      [$push/int source]]
            [:nil      [$push/nil]]
            [otherwise [$push/v source]]]]

[defn bytecompile/quote [source]
      [case [type-of source]
            [:int      [$push/int source]]
            [:symbol   [$push/symbol source]]
            [otherwise [$push/v source]]]]

[defn bytecompile/do/form [source env]
      [when source [cons [cons [bytecompile* [car source] env]
                               [if [last? source] #nil
                                   [cons [$drop] #nil]]]
                         [bytecompile/do/form [cdr source] env]]]]

[defn bytecompile/do [source env]
      [list [bytecompile/do/form [cdr source] env]]]

[defn bytecompile/def [source env]
      [when [or [not [symbol? [cadr source]]]
                [not [cddr source]]]
        [throw [list :type-error
                     "[def] needs a symbol name and a value as arguments" #nil env]]]
      [list [bytecompile* [caddr source] env]
            [$def [cadr source]]]]

[defn bytecompile/set! [source env]
      [when [or [not [symbol? [cadr source]]]
                [not [cddr source]]]
        [throw [list :type-error
                     "[set!] needs a symbol name and a value as arguments" #nil env]]]
      [list [bytecompile* [caddr source] env]
            [$set [cadr source]]]]

[defn bytecompile/if [source env]
      [let [[sym-else [bytecompile/gen-label]]
            [sym-after [bytecompile/gen-label]]]
        [list [bytecompile* [cadr source] env]
              [$jf sym-else]
              [bytecompile* [caddr source] env]
              [$jmp sym-after]
              [list :label sym-else]
              [bytecompile* [cadddr source] env]
              [list :label sym-after]]]]

[defn bytecompile/while [source env]
      [def sym-start [bytecompile/gen-label]]
      [def sym-end [bytecompile/gen-label]]
      [list [$push/nil] ;; Return value if predicate is #f from the beginning
            [$roots/save]
            [$jmp sym-end]
            [list :label sym-start]
            [$drop]
            [$roots/restore]
            [bytecompile/do/form [cddr source] env]
            [list :label sym-end]
            [bytecompile* [cadr source] env]
            [$jt sym-start]]]

[defn bytecompile/procedure/arg [source env]
      [if [last? source]
          [bytecompile* [car source] env]
          [cons [bytecompile* [car source] env]
                [bytecompile/procedure/arg [cdr source] env]]]]

[defn bytecompile/procedure/inline? [op]
      [case [type-of op]
            [:lambda [meta op :inline]]
            [#t #f]]]

[defn bytecompile/procedure/inline [op args env]
      [def arg-count [length args]]
      [when [> arg-count 1]
        [throw [list :compiler-error "For now only monadic functions can be inlined" op [current-lambda]]]]
      [def form [macroexpand/do [meta op :source] env]]
      [def arg-name [car [closure/arguments op]]]
      [if args
          [bytecompile* [list/replace form arg-name [car args]] env]
          [bytecompile* form env]]]

[defn bytecompile/procedure [op args env]
      [if [bytecompile/procedure/inline? op]
          [bytecompile/procedure/inline op args env]
          [bytecompile/procedure/default op args env]]]

[defn bytecompile/procedure/default [op args env]
      [def arg-count [length args]]
      [if [$apply/optimize? op arg-count]
          [list [when args [bytecompile/procedure/arg args]]
                [$apply arg-count op]]
          [list [bytecompile* op env]
                [when args [bytecompile/procedure/arg args]]
                [$apply/dynamic arg-count]]]]

[defn bytecompile/and/rec [source env label-end]
      [list [bytecompile* [car source] env]
            [when [cdr source]
              [list [$dup]
                    [$jf label-end]
                    [$drop]
                    [bytecompile/and/rec [cdr source] env label-end]]]]]

[defn bytecompile/and [source env]
      [def label-end [bytecompile/gen-label]]
      [list [bytecompile/and/rec [cdr source] env label-end]
            [list :label label-end]]]

[defn bytecompile/or/rec [source env label-end]
      [when source[list [bytecompile* [car source] env]
                        [$dup]
                        [$jt label-end]
                        [$drop]
                        [bytecompile/or/rec [cdr source] env label-end]]]]

[defn bytecompile/or [source env]
      [def label-end [bytecompile/gen-label]]
      [list [bytecompile/or/rec [cdr source] env label-end]
            [$push/lval #f]
            [list :label label-end]]]

[defn bytecompile/string [source env]
      [bytecompile/procedure cat source env]]

[defn bytecompile/array [source env]
      [bytecompile/procedure array/ref source env]]

[defn bytecompile/tree [source env]
      [bytecompile/procedure tree/ref source env]]

[defn bytecompile/fn* [source env]
      [def arg-count [length [cdr source]]]
      [when [!= arg-count 5] [exception :arity-error "[fn*] needs exactly 4 arguments" source]]
      [cdr! source]
      [list [bytecompile* [car source] env]
            [bytecompile* [cadr source] env]
            [bytecompile* [caddr source] env]
            [bytecompile* [cadddr source] env]
            [$fn/dynamic]]]

[defn bytecompile/macro* [source env]
      [def arg-count [length [cdr source]]]
      [when [!= arg-count 5] [exception :arity-error "[macro*] needs exactly 4 arguments" source]]
      [cdr! source]
      [list [bytecompile* [car source] env]
            [bytecompile* [cadr source] env]
            [bytecompile* [caddr source] env]
            [bytecompile* [cadddr source] env]
            [$macro/dynamic]]]

[defn bytecompile/ω*  [source env]
      [list [$let]
            [bytecompile/do/form [cdr source] env]
            [$drop]
            [$closure/push]
            [$closure/pop]]]

[defn bytecompile/let*  [source env]
      [list [$let]
            [bytecompile/do [cadr source] env]
            [$closure/pop]]]

[defn bytecompile/return [source env]
      [list [bytecompile* [cadr source] env]
            [$ret]]]

[defn bytecompile/try  [source env]
      [def end-sym   [bytecompile/gen-label]]
      [list [bytecompile* [cadr source] env]
            [$try end-sym]
            [bytecompile/do/form [cddr source] env]
            [$closure/pop]
            [list :label end-sym]]]

[defn bytecompile* [source env]
      "Compile the forms in source"
      [def op [if [resolves? [car source] env]
                  [resolve [car source] env]
                  [car source]]]
      [case [type-of op]
            [[:lambda :native-function]
             [case op
                   [do     [bytecompile/do      source env]]
                   [let*   [bytecompile/let*    source env]]
                   [def    [bytecompile/def     source env]]
                   [set!   [bytecompile/set!    source env]]
                   [if     [bytecompile/if      source env]]
                   [while  [bytecompile/while   source env]]
                   [and    [bytecompile/and     source env]]
                   [or     [bytecompile/or      source env]]
                   [fn*    [bytecompile/fn*     source env]]
                   [macro* [bytecompile/macro*  source env]]
                   [ω*    [bytecompile/ω*     source env]]
                   [try    [bytecompile/try     source env]]
                   [return [bytecompile/return  source env]]
                   [quote  [bytecompile/quote   [cadr source]]]
                   [otherwise [bytecompile/procedure op [cdr source] env]]]]
            [[:pair :symbol]            [bytecompile/procedure op [cdr source] env]]
            [:string                    [bytecompile/string source env]]
            [:array                     [bytecompile/array source env]]
            [:tree                      [bytecompile/tree source env]]
            [otherwise                  [bytecompile/literal source]]]]

[defn bytecompile [form environment]
      [list [bytecompile* form environment]
            [$ret]]]