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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
|
//! Process library files and extracts library specifications
use std::collections::btree_map::Iter;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::env;
use std::fs;
use std::io::{BufRead, BufReader, Error, ErrorKind, Write};
use crate::spec_map::{Bounds, LibSpecs, ProvidedOps};
const LIBSPECNAME: &str = "/*LIBSPEC-NAME*";
const LIBSPECNAMEEND: &str = "*ENDLIBSPEC-NAME*/";
const LIBSPEC: &str = "/*LIBSPEC*";
const LIBSPECEND: &str = "*ENDLIBSPEC*/";
const LANGDECL: &str = "#lang rosette\n";
const GENPATH: &str = "./racket_specs/gen_lib_spec/";
const OPNAME: &str = "/*OPNAME*";
const OPNAMEEND: &str = "*ENDOPNAME*/";
const IMPL: &str = "/*IMPL*";
const IMPLEND: &str = "*ENDIMPL*/";
type ErrorMessage = String;
fn is_next_pragma_impl(src: &str) -> bool {
match src.find(IMPL) {
Some(impl_pos) => match src.find(LIBSPEC) {
Some(spec_pos) => impl_pos < spec_pos,
None => true,
},
None => false,
}
}
fn has_pragma_impl(src: &str) -> bool {
src.contains(IMPL)
}
fn has_pragma_spec(src: &str) -> bool {
src.contains(LIBSPEC)
}
/// Specifications extracted from a library file
pub struct LibraryFileSpec {
/// Name of the specification
spec_name: String,
/// Name of the specified structs
struct_name: String,
/// All specification code defined
specs: Vec<String>,
/// The provided rosette module name
provide: String,
/// The bounds of each operation
interface_provide_map: Bounds,
/// The provided operations
provided_ops: ProvidedOps,
}
/// Read a library file and parse the spec
pub fn read_lib_file(filename: String) -> Result<LibraryFileSpec, ErrorMessage> {
let contents = fs::read_to_string(filename).expect("Something went wrong reading the file");
let trimed_contents = contents.trim().to_string();
let name_pragmas: Vec<&str> = trimed_contents.matches(LIBSPECNAME).collect();
let name_end_pragmas: Vec<&str> = trimed_contents.matches(LIBSPECNAMEEND).collect();
let spec_pragmas: Vec<&str> = trimed_contents.matches(LIBSPEC).collect();
let spec_end_pragmas: Vec<&str> = trimed_contents.matches(LIBSPECEND).collect();
if ((name_pragmas.len() != 1) || (name_end_pragmas.len() != 1)) {
Err("Error, invalid declaration of library specification name**.".to_string())
} else if (spec_pragmas.len() != spec_end_pragmas.len()) {
return Err("Error, invalid declaration of library specification.".to_string());
} else {
let mut specs = String::new();
let v1: Vec<&str> = trimed_contents.split(LIBSPECNAME).collect();
let s = v1
.get(1)
.expect("Error, invalid declaration of library specification name.");
let v2: Vec<&str> = s.split(LIBSPECNAMEEND).collect();
let s3 = v2.first().unwrap().trim().to_string();
let v3: Vec<&str> = s3.split(' ').collect();
let spec_name = v3.first().unwrap().trim().to_string();
let struct_name = v3.get(1).unwrap().trim().to_string();
let s1 = v1.first().expect("Unexpected error.");
let s2 = v2.get(1).expect("Unexpected error.");
// process interface blocks
let mut trimed_contents = String::new();
trimed_contents.push_str(s1);
trimed_contents.push_str(s2);
if (!is_next_pragma_impl(&trimed_contents) && has_pragma_spec(&trimed_contents)) {
return Err("Specification without declared interface is not allowed".to_string());
} else {
let mut interfaces = Vec::<String>::new();
let mut interface_info =
HashMap::<String, BTreeMap<String, (String, String, String)>>::new();
let mut code = Vec::<String>::new();
let mut provided_ops = Vec::<String>::new();
while (has_pragma_impl(&trimed_contents)) {
let v4: Vec<&str> = trimed_contents.splitn(2, IMPL).collect();
let s4 = v4.get(1).expect("Error, invalid interface declaration.");
let v5: Vec<&str> = s4.splitn(2, IMPLEND).collect();
let mut interface_name = v5.first().unwrap().trim().to_string();
trimed_contents = v5.get(1).unwrap().trim().to_string();
let lib_specs = extract_lib_specs(trimed_contents);
match lib_specs {
Ok(LibSpec {
contents,
code: mut result,
op_infos,
provided_ops: mut ops,
}) => {
code.append(&mut result);
interface_info.insert(interface_name.clone(), op_infos);
interfaces.push(interface_name.clone());
provided_ops.append(&mut ops);
trimed_contents = contents;
}
Err(e) => {
return Err(e);
}
}
}
let (provide, interface_provide_map) = generate_provide(interface_info);
Ok(LibraryFileSpec {
spec_name,
struct_name,
specs: code.clone(),
provide,
interface_provide_map,
provided_ops: (code, provided_ops),
})
}
}
}
pub fn generate_provide(
interface_info: HashMap<String, BTreeMap<String, (String, String, String)>>,
) -> (String, Bounds) {
let mut interfaces = Vec::<String>::new();
let mut provide = String::new();
let mut interface_provide_map = Bounds::new();
for (interface, infos) in interface_info.iter() {
let mut specs = Vec::<String>::new();
let mut pres = Vec::<String>::new();
for (key, value) in infos.iter() {
specs.push(value.0.clone());
pres.push(value.1.clone());
}
let specs_name = interface.to_lowercase() + "-specs";
let pres_name = interface.to_lowercase() + "-pres";
let interface_name = interface.to_lowercase();
let specs_str =
"\n(define ".to_string() + &specs_name + " (list " + &specs.join(" ") + "))\n";
let pres_str = "(define ".to_string() + &pres_name + " (list " + &pres.join(" ") + "))\n";
let interface_str = "(define ".to_string()
+ &interface_name
+ " (cons "
+ &specs_name
+ " "
+ &pres_name
+ "))\n";
provide = provide + &specs_str + &pres_str + &interface_str;
interfaces.push(interface.to_lowercase());
interface_provide_map.insert(interface.to_string(), interface_name);
}
let provide_str = "(provide ".to_string() + &interfaces.join(" ") + ")";
provide = provide + &provide_str;
(provide, interface_provide_map)
}
pub struct LibSpec {
/// The contents of the LIBSPEC block
contents: String,
/// The code define by the block
code: Vec<String>,
/// Hoare triples for each operation
op_infos: BTreeMap<String, (String, String, String)>,
/// Provided operation names
provided_ops: Vec<String>,
}
/// Extract a spec from a LIBSPEC block
pub fn extract_lib_specs(src: String) -> Result<LibSpec, ErrorMessage> {
let mut result = Vec::<String>::new();
let mut contents = src.trim();
let mut op_infos = BTreeMap::<String, (String, String, String)>::new();
let mut provided_ops = Vec::<String>::new();
while (!contents.is_empty() && !is_next_pragma_impl(contents)) {
if (contents.contains(LIBSPEC) && contents.contains(LIBSPECEND)) {
let v1: Vec<&str> = contents.splitn(2, LIBSPEC).collect();
let s = v1.get(1).expect("Error, invalid specification.");
let v2: Vec<&str> = s.splitn(2, LIBSPECEND).collect();
let spec = v2.first().unwrap().trim().to_string();
let info = extract_op_info(spec.clone()).unwrap();
op_infos.insert(info.0, (info.1.clone(), info.2, info.3));
provided_ops.push(info.1);
let v3: Vec<&str> = spec.splitn(2, OPNAMEEND).collect();
let code = v3
.get(1)
.unwrap()
.trim_matches(|c| c == '\t' || c == ' ')
.to_string();
result.push(code);
contents = v2.get(1).unwrap().trim();
} else {
break;
}
}
Ok(LibSpec {
contents: contents.to_string(),
code: result,
op_infos,
provided_ops,
})
}
pub fn extract_op_info(spec: String) -> Result<(String, String, String, String), ErrorMessage> {
let op_name_pragmas: Vec<&str> = spec.matches(OPNAME).collect();
let op_name_end_pragmas: Vec<&str> = spec.matches(OPNAMEEND).collect();
if (spec.starts_with('/') && op_name_pragmas.len() == 1 && op_name_end_pragmas.len() == 1) {
let v1: Vec<&str> = spec.split(OPNAME).collect();
let s = v1
.get(1)
.expect("Error, invaild operation information declaration.");
let v2: Vec<&str> = s.split(OPNAMEEND).collect();
let info_string = v2
.first()
.expect("Error, invaild operation information declaration.");
let mut infos: Vec<&str> = info_string.trim().split(' ').collect();
if (infos.len() == 4) {
let post = infos.pop().unwrap();
let pre = infos.pop().unwrap();
let op_spec = infos.pop().unwrap();
let name = infos.pop().unwrap();
Ok((
name.to_string(),
op_spec.to_string(),
pre.to_string(),
post.to_string(),
))
} else {
Err("Error, invaild operation information declaration.".to_string())
}
} else {
Err("Error, invaild operation information declaration.".to_string())
}
}
pub fn write_lib_file(filename: String, contents: &[String], provide: String) -> Result<(), Error> {
let path = GENPATH;
let mut output = fs::File::create(path.to_owned() + &filename)?;
write!(output, "{}", LANGDECL)?;
for item in contents.iter() {
write!(output, "{}", item)?;
}
write!(output, "{}", provide)?;
Ok(())
}
pub fn process_lib_spec(
filename: String,
) -> Result<(String, String, Bounds, ProvidedOps), ErrorMessage> {
let result = read_lib_file(filename);
match result {
Ok(LibraryFileSpec {
spec_name,
struct_name,
specs,
provide,
interface_provide_map,
provided_ops,
}) => {
let spec_name = spec_name + ".rkt";
let state = write_lib_file(spec_name.clone(), &specs, provide);
if state.is_err() {
return Err("Unable to create lib specification file".to_string());
}
Ok((spec_name, struct_name, interface_provide_map, provided_ops))
}
Err(e) => Err(e),
}
}
pub fn process_lib_specs(dirname: String) -> Result<LibSpecs, ErrorMessage> {
let paths = fs::read_dir(dirname).unwrap();
let files: Vec<String> = paths
.into_iter()
.map(|path| path.unwrap().path().to_str().unwrap().to_string())
.filter(|path| !path.contains("/mod.rs"))
.collect();
let mut lib_specs = LibSpecs::new();
for path in files {
match process_lib_spec(path) {
Ok((spec_name, struct_name, interface_provide_map, provided_ops)) => {
lib_specs.insert(
struct_name,
(spec_name, interface_provide_map, provided_ops),
);
}
Err(e) => {
return Err(e);
}
}
}
Ok(lib_specs)
}
|