File: primitive.rb

package info (click to toggle)
mikutter 5.1.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,780 kB
  • sloc: ruby: 22,912; sh: 186; makefile: 21
file content (182 lines) | stat: -rw-r--r-- 4,862 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# -*- coding: utf-8 -*-
require_relative 'atom'
require_relative 'error'
require_relative 'macro'
require_relative 'to_ruby'

module MIKU
  class Primitive
    include Atom
    include ToRuby

    def initialize(func)
      @func = func.to_sym
    end

    def call(*args)
      send(@func, *args)
    end

    def self.injecting_method(name, method = nil)
      method = name unless method
      define_method(name){ |symtable, *objects|
        unless objects.empty?
          first, *rest = *objects
          rest.inject(eval(symtable, first)){|a, b|
            a.__send__(method, eval(symtable, b)) } end } end

    def self.consing_method(name, method = nil)
      method = name unless method
      define_method(name){ |symtable, *objects|
        unless objects.empty?
          objects.map{ |x| eval(symtable, x) }.enum_for(:each_cons, 2).all?{ |a|
            a[0].__send__(method, a[1]) } end } end

    injecting_method(:+)
    injecting_method(:-)
    injecting_method(:*)
    injecting_method(:/)

    consing_method(:<)
    consing_method(:>)
    consing_method(:<=)
    consing_method(:>=)
    consing_method(:eq, :equal?)
    consing_method(:eql, :==)
    consing_method(:equal, :===)

    def backquote(symtable, val)
      result = []
      val.each{|n|
        if not n.is_a?(List) then
          result << n
        elsif n.car == :comma then
          result << eval(symtable, n[1])
        elsif n.car == :comma_at then
          list = eval(symtable, n[1])
          raise ExceptionDelegator.new(',@がリスト以外に対して適用されました', ArgumentError) if not list.is_a?(List)
          result.concat(list.to_a) if list
        else
          result << backquote(symtable, n)
        end
      }
      result
    end

    def cons(symtable, head, tail)
      Cons.new(eval(symtable, head), eval(symtable, tail))
    end

    def eval(symtable, node)
      miku_eval_another(symtable, node)
    end

    def _not(symtable, sexp)
      not eval(symtable, sexp) end

    def if(symtable, condition, true_case, false_case = nil)
      if(eval(symtable, condition)) then
        eval(symtable, true_case)
      else
        eval(symtable, false_case)
      end
    end

    def list(symtable, *args)
      args.map{|n| eval(symtable, n) }.to_cons
    end

    def listp(symtable, val)
      eval(symtable, val).is_a?(List)
    end

    def quote(symtable, val)
      val
    end

    def set(symtable, key, val, *args)
      raise ExceptionDelegator.new('setに与える引数は偶数個にして下さい', ArgumentError) if args.size == 1
      key = eval(symtable, key)
      val = eval(symtable, val)
      symtable.set(key, val)
      return val if args.empty?
      set(symtable, *args)
    end

    def defun(symtable, key, val, *args)
      raise ExceptionDelegator.new('defunに与える引数は偶数個にして下さい', ArgumentError) if args.size == 1
      key = eval(symtable, key)
      val = eval(symtable, val)
      symtable.defun(key, val)
      return val if args.empty?
      defun(symtable, *args)
    end

    def function(symtable, symbol)
      if symbol.is_a? Symbol
        symtable[symbol].cdr
      else
        symbol end end

    def macro(symtable, alist, *body)
      Macro.new(alist, body)
    end

    def macro_expand_all(symtable, sexp)
      macro_expand_all_ne(symtable, eval(symtable, sexp))
    end

    def macro_expand_all_ne(symtable, sexp)
      if sexp.is_a?(List)
        expanded = macro_expand_ne(symtable, sexp)
        case expanded
        in [Symbol => macro, *] if symtable[macro].cdr.is_a?(Macro)
          macro_expand_all_ne(symtable, expanded)
        in List
          expanded.map(&method(:macro_expand_all_ne).curry.(symtable))
        else
          expanded
        end
      else
        sexp
      end
    end

    def macro_expand_ne(symtable, sexp)
      if sexp.is_a? List
        macro = if(sexp.car.is_a? Symbol)
                  symtable[sexp.car].cdr
                else
                  eval(symtable, sexp.car) end
        if macro.is_a?(Macro)
          macro.macro_expand(*sexp.cdr)
        else
          sexp end
      else
        sexp end end

    def to_ruby(symtable, sexp)
      MIKU::ToRuby.to_ruby(macro_expand_all(symtable, sexp))
    end

    def to_ruby_ne(symtable, sexp)
      MIKU::ToRuby.to_ruby(macro_expand_all_ne(symtable, sexp))
    end

    def macro_expand(symtable, sexp)
      macro_expand_ne(symtable, eval(symtable, sexp))
    end

    def negi(parenttable, alist, *body)
      # body = body.map{ |node| macro_expand(parenttable, node) }
      lambda{ |*args|
        symtable = parenttable.miracle_binding(alist, args)
        body.inject(nil){ |last, operator|
          eval(symtable, operator) } } end

    def require_runtime_library(symtable, filename)
      require eval(symtable, filename)
    end

  end
end