File: symboltable.rb

package info (click to toggle)
mikutter 3.8.6%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 10,544 kB
  • sloc: ruby: 20,548; sh: 99; makefile: 19
file content (110 lines) | stat: -rw-r--r-- 4,005 bytes parent folder | download | duplicates (5)
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
# -*- coding: utf-8 -*-
require_relative 'error'

module MIKU
  class SymbolTable < Hash

    INITIALIZE_FILE = File.expand_path(File.join(File.dirname(__FILE__), 'init.miku'))

    # :caller-file "呼び出し元ファイル名"
    # :caller-line 行
    # :caller-function :関数名
    def initialize(parent = nil, default = {})
      if parent
        @parent = parent
      else
        @parent = MIKU::SymbolTable.initialized_table end
      if(SymbolTable.defaults.equal?(parent))
        def self.ancestor
          self end end
      super(){ |this, key| @parent[key.to_sym] }
      merge!(default) unless default.empty?
    end

    def ancestor
      @parent.ancestor end

    def []=(key, val)
      if not(key.is_a?(Symbol)) then
        raise ExceptionDelegator.new("#{key.inspect} に値 #{val.inspect} を代入しようとしました", TypeError) end
      super(key, val) end

    def bind(key, val, setfunc)
      cons = self[key]
      if cons
        cons.method(setfunc).call(val)
      else
        self[key] = nil.method(setfunc).call(val) end end

    def set(key, val)
      if not(key.is_a?(Symbol)) then
        raise ExceptionDelegator.new("#{key.inspect} に値 #{val.inspect} を代入しようとしました", TypeError) end
      bind(key.to_sym, val, :setcar) end

    def defun(key, val)
      if not(key.is_a?(Symbol)) then
        raise ExceptionDelegator.new("#{key.inspect} に関数 #{val.inspect} を代入しようとしました", TypeError) end
      bind(key.to_sym, val, :setcdr) end

    def miracle_binding(keys, values)
      _miracle_binding(SymbolTable.new(self), keys, values) end

    def _miracle_binding(symtable, keys, values)
      if(keys.is_a? Enumerable and values.is_a? Enumerable)
        key = keys.car
        val = values.car
        if key.is_a? List
          if key[0] == :rest
            symtable[key[1]] = Cons.new(values)
            return symtable end
        else
          symtable[key] = Cons.new(val) end
        _miracle_binding(symtable, keys.cdr, values.cdr) end
      symtable end

    def self.initialized_table
      @@initialized_table ||= (@@initialized_table = MIKU::SymbolTable.new(SymbolTable.defaults)).run_init_script end

    def self.defsform(fn=nil, *other)
      return [] if fn == nil
      [fn , Cons.new(nil, Primitive.new(fn))] + defsform(*other) end

    def self.defun(fn=nil, *other)
      return [] if fn == nil
      [fn , Cons.new(nil, fn)] + defun(*other) end

    def self.consts
      Module.constants.map{ |c| [c.to_sym, Cons.new(eval(c.to_s))] }.inject([]){ |a, b| a + b } end
    # Module.constants.map{ |c| [c.to_sym, Cons.new(eval(c))] }.inject([]){ |a, b| a + b } end

    def self.defaults
      @@defaults ||= __defaults end

    def self.__defaults
      default = Hash[*(defsform(:cons, :listp, :set, :function, :value, :quote, :eval, :list,
                                :if, :backquote, :macro, :require_runtime_library, :+, :-, :*, :/,
                                :<, :>, :<=, :>=, :eq, :eql, :equal) +
                       [:lambda , Cons.new(nil, Primitive.new(:negi)),
                        :def , Cons.new(nil, Primitive.new(:defun)),
                        :'macro-expand' , Cons.new(nil, Primitive.new(:macro_expand)),
                        :'macro-expand-all' , Cons.new(nil, Primitive.new(:macro_expand_all)),
                        :'to-ruby' , Cons.new(nil, Primitive.new(:to_ruby)),
                        :"=", Cons.new(nil, Primitive.new(:eq)),
                        :not, Cons.new(nil, Primitive.new(:_not)),
                        :true, Cons.new(true, nil),
                        :false, Cons.new(false, nil)
                       ] + consts)].freeze
      Hash.new{ |h, k|
        if default[k]
          h[k] = default[k]
        elsif /^[A-Z][a-zA-Z0-9_]*/ =~ k.to_s and klass = eval(k.to_s)
          h[k] = klass
        end }
    end

    def run_init_script
      miku_stream(File.open(INITIALIZE_FILE), self)
      self end

  end
end