File: parser.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 (170 lines) | stat: -rw-r--r-- 4,676 bytes parent folder | download | duplicates (8)
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
# -*- coding: utf-8 -*-
# hatsune lisp is moest language

require_relative 'miku'
require_relative 'error'
require 'stringio'

module MIKU

  def self.parse(str)
    if(str.is_a?(String))
      _parse(StringIO.new(str, 'r').extend(StaticCode))
    else
      str.extend(StaticCode) if not str.is_a? StaticCode
      str.staticcode_file = str.path if defined? str.path
      _parse(str)
    end
  end

  def self._parse(s)
    while(true) do
      c = skipspace(s)
      if c == ';'
        _comment(s)
      else
        break end end
    pos = s.staticcode_dump
    r = case c
        when '(' then
          _list(s)
        when '#' then
          _structure(s)
        when '"' then
          _string(s)
        when '`' then
          Cons.list(:backquote, _parse(s)).extend(StaticCode).staticcode_copy_info(pos)
        when ',' then
          c = s.getc.chr
          if c == '@' then
            Cons.list(:comma_at, _parse(s)).extend(StaticCode).staticcode_copy_info(pos)
          else
            s.ungetc(c[0])
            Cons.list(:comma, _parse(s)).extend(StaticCode).staticcode_copy_info(pos)
          end
        when '\'' then
          Cons.list(:quote, _parse(s)).extend(StaticCode).staticcode_copy_info(pos)
        else
          _symbol(c, s)
        end
    _after(s, r)
  end

  def self._after(s, r)
    read_to(s){ |c| not(c =~ /[\t ]/) }
    skipped = s.getc
    if skipped.respond_to?(:chr) and skipped.chr == "["
      pos = s.staticcode_dump
      return _after(s, Cons.new(r, _list(s, ']').extend(StaticCode).staticcode_copy_info(pos)
                                ).extend(StaticCode).staticcode_copy_info(pos))
    else
      s.ungetc(skipped) if skipped
    end
    r
  end

  def self._comment(s)
    read_to(s){ |c| c == "\n" }
  end

  def self._structure(s)
    c = skipspace(s)
    pos = s.staticcode_dump
    if(c == '(')
      Cons.new(:lambda, _list(s)).extend(StaticCode).staticcode_copy_info(pos)
    else
      type = _symbol(c, s)
      c = skipspace(s)
      if c != '('
        raise SyntaxError.new("##{type}の後に文字#{c}があります。必ず中括弧をおいてください",s)
      end
      pos = s.staticcode_dump
      lst = _list(s).extend(StaticCode).staticcode_copy_info(pos)
      case
      when [:array, :a].include?(type)
        lst.to_a.extend(StaticCode).staticcode_copy_info(pos)
      when [:hash, :h].include?(type)
        genlist = Cons.new(:list, lst).extend(StaticCode).staticcode_copy_info(pos)
        Cons.list(:to_hash, genlist).extend(StaticCode).staticcode_copy_info(pos)
        # Hash[*lst.to_a].extend(StaticCode).staticcode_copy_info(pos)
      when [:lambda, :function, :func, :f].include?(type)
        Cons.new(:lambda, lst).extend(StaticCode).staticcode_copy_info(pos) end end end

  def self._list(s, pend=')')
    scd = s.staticcode_dump
    c = s.getc.chr
    return nil if c == pend
    s.ungetc(c[0])
    car = _parse(s)
    c = skipspace(s)
    if(c == '.') then
      cdr = _parse(s)
      s.ungetc(skipspace(s)[0])
      raise SyntaxError.new('ドット対がちゃんと終わってないよ',s) if(s.getc.chr != pend)
      return Cons.new(car, cdr).extend(StaticCode).staticcode_copy_info(scd)
    else
      s.ungetc(c[0])
      return Cons.new(car, _list(s, pend)).extend(StaticCode).staticcode_copy_info(scd)
    end
  end

  def self._string(s)
    result = read_to(s){ |c| c == '"' }
    s.getc
    result.extend(StaticCode).staticcode_copy_info(s)
  end

  def self._symbol(c, s)
    sym = c + read_to(s){ |c| not(c =~ /[^\(\)\{}\[\].',#\s]/) }
    raise SyntaxError.new('### 深刻なエラーが発生しました ###',s) if not(sym)
    if(sym =~ /^-?[0-9]+$/) then
      sym.to_i
    elsif(sym =~ /^-?[0-9]+\.[0-9]+$/) then
      sym.to_f
    elsif(sym == 'nil') then
      nil
    elsif(sym == '')
      raise MIKU::EndofFile
    else
      sym.to_sym
    end
  end

  def self.read_to(s, escape=false, &cond)
    c = s.getc
    return '' if not c
    c = c.chr
    if !escape and cond.call(c)
      s.ungetc(c[0])
      return '' end
    case
    when '\\' == c
      read_to(s, true, &cond)
    when ('n' == c) && escape
      "\n" + read_to(s, &cond)
    else
      c + read_to(s, &cond) end
  end

  def self.skipspace(s)
    c = s.getc
    return '' if not c
    c = c.chr
    s.staticcode_line += 1 if c == "\n"
    return skipspace(s) if(c =~ /\s/)
    c
  end

  def self.unparse(val)
    if val === nil
      'nil'
    elsif(val.is_a?(List))
      val.unparse
    elsif(val.is_a?(String))
      '"' + val.dup.gsub("\n", '\\n').gsub('"', '\\"') + '"'
    elsif(val.respond_to?(:unparse))
      val.unparse
    else
      val.inspect end end

end