make: revert/correct fix for plain undef symbol.
[jackhill/mal.git] / vala / reader.vala
CommitLineData
213a3288
ST
1class Mal.Reader : GLib.Object {
2 static Regex tok_re;
3 static Regex tok_num;
4
5 int origlen;
6 string data;
7 int pos;
8
9 string next_token;
10
11 static construct {
12 tok_re = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;[^\n]*|[^\s\[\]{}('"`,;)]*)/; // comment to unconfuse emacs vala-mode "]);
13 tok_num = /^-?[0-9]/;
14 }
15
16 private string poserr(string fmt, ...) {
17 return "char %d: %s".printf(origlen - data.length,
18 fmt.vprintf(va_list()));
19 }
20
21 private void advance() throws Error {
22 do {
23 MatchInfo info;
24 if (!tok_re.match(data, 0, out info))
25 throw new Error.BAD_TOKEN(poserr("bad token"));
26
27 next_token = info.fetch(1);
28 int tokenend;
29 info.fetch_pos(1, null, out tokenend);
30 data = data[tokenend:data.length];
31 } while (next_token.has_prefix(";"));
32 }
33
34 public Reader(string str) throws Error {
35 data = str;
36 origlen = data.length;
37 pos = 0;
38 advance();
39 }
40
41 public string peek() throws Error {
42 return next_token;
43 }
44
45 public string next() throws Error {
46 advance();
47 return peek();
48 }
49
50 public static Mal.Val? read_str(string str) throws Error {
51 var rdr = new Reader(str);
52 if (rdr.peek() == "")
53 return null;
54 var toret = rdr.read_form();
55 if (rdr.peek() != "")
56 throw new Mal.Error.PARSE_ERROR(
57 rdr.poserr("trailing junk after expression"));
58 return toret;
59 }
60
61 public Mal.Val read_form() throws Error {
62 string token = peek();
63 if (token == "(") {
64 next(); // eat (
65 return new Mal.List(read_list(")"));
66 } else {
67 return read_atom();
68 }
69 }
70
71 public GLib.List<Mal.Val> read_list(string endtok) throws Error {
72 var list = new GLib.List<Mal.Val>();
73 string token;
74 while (true) {
75 token = peek();
76 if (token == "")
77 throw new Mal.Error.PARSE_ERROR(poserr("unbalanced parens"));
78 if (token == endtok) {
79 next(); // eat end token
80 return list;
81 }
82
83 list.append(read_form());
84 }
85 }
86
87 public Mal.Hashmap read_hashmap() throws Error {
88 var map = new Mal.Hashmap();
89 string token;
90 while (true) {
91 Mal.Val vals[2];
92 for (int i = 0; i < 2; i++) {
93 token = peek();
94 if (token == "")
95 throw new Mal.Error.PARSE_ERROR(
96 poserr("unbalanced braces"));
97 if (token == "}") {
98 if (i != 0)
99 throw new Mal.Error.PARSE_ERROR(
100 poserr("odd number of elements in hashmap"));
101
102 next(); // eat end token
103 return map;
104 }
105
106 vals[i] = read_form();
107 }
108 map.insert(vals[0], vals[1]);
109 }
110 }
111
112 public Mal.Val read_atom() throws Error {
113 string token = peek();
114 next();
115 if (tok_num.match(token))
116 return new Mal.Num(int64.parse(token));
117 if (token.has_prefix(":"))
118 return new Mal.Keyword(token[1:token.length]);
119 if (token.has_prefix("\"")) {
f59776b1 120 if (token.length < 2 || !token.has_suffix("\""))
213a3288
ST
121 throw new Mal.Error.BAD_TOKEN(
122 poserr("end of input in mid-string"));
123
124 token = token[1:token.length-1];
125
126 int end = 0;
127 int pos = 0;
128 string strval = "";
129
130 while ((pos = token.index_of ("\\", end)) != -1) {
131 strval += token[end:pos];
f59776b1
ST
132 if (token.length - pos < 2)
133 throw new Mal.Error.BAD_TOKEN(
134 poserr("end of input in mid-string"));
213a3288
ST
135 switch (token[pos:pos+2]) {
136 case "\\\\":
137 strval += "\\"; break;
138 case "\\\"":
139 strval += "\""; break;
140 case "\\n":
141 strval += "\n"; break;
142 }
143 end = pos+2;
144 }
145 strval += token[end:token.length];
146 return new Mal.String(strval);
147 }
148 switch (token) {
149 case "nil":
150 return new Mal.Nil();
151 case "true":
152 return new Mal.Bool(true);
153 case "false":
154 return new Mal.Bool(false);
155 case "[":
156 return new Mal.Vector.from_list(read_list("]"));
157 case "{":
158 return read_hashmap();
159 case "'":
160 case "`":
161 case "~":
162 case "~@":
163 case "@":
164 var list = new GLib.List<Mal.Val>();
165 list.append(new Mal.Sym(
166 token == "'" ? "quote" :
167 token == "`" ? "quasiquote" :
168 token == "~" ? "unquote" :
169 token == "~@" ? "splice-unquote" : "deref"));
170 list.append(read_form());
171 return new Mal.List(list);
172 case "^":
173 var list = new GLib.List<Mal.Val>();
174 list.append(new Mal.Sym("with-meta"));
175 var metadata = read_form();
176 list.append(read_form());
177 list.append(metadata);
178 return new Mal.List(list);
179 default:
180 return new Mal.Sym(token);
181 }
182 }
183}