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
|