rural 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/vlerq_ext.c ADDED
@@ -0,0 +1,394 @@
1
+ /*
2
+ * vlerq_ext.c - Interface to the Ruby scripting language.
3
+ */
4
+
5
+ #include <ruby.h>
6
+
7
+ #include <stdlib.h>
8
+ #include <string.h>
9
+
10
+ #include "vlerq.h"
11
+
12
+ typedef struct CmdDispatch {
13
+ const char *name, *args;
14
+ ItemTypes (*proc) (Item_p);
15
+ } CmdDispatch;
16
+
17
+ static CmdDispatch f_commands[] = {
18
+ #include "vlerq_ext.h"
19
+ };
20
+
21
+ Shared *GetShared () {
22
+ static Shared f_shared;
23
+ return &f_shared;
24
+ }
25
+
26
+ void FailedAssert (const char *msg, const char *file, int line) {
27
+ rb_fatal("Failed assertion at %s, line %d: %s\n", file, line, msg);
28
+ }
29
+
30
+ typedef struct { VALUE view; int row; } Row, *Row_p;
31
+
32
+ static VALUE cRow;
33
+
34
+ static VALUE row_v (VALUE self) {
35
+ Row_p p;
36
+ Data_Get_Struct(self, Row, p);
37
+ return p->view;
38
+ }
39
+
40
+ static VALUE row_i (VALUE self) {
41
+ Row_p p;
42
+ Data_Get_Struct(self, Row, p);
43
+ return INT2NUM(p->row);
44
+ }
45
+
46
+ static void row_mark (Row_p p) {
47
+ rb_gc_mark(p->view);
48
+ }
49
+
50
+ static VALUE AtRow (VALUE view, int row) {
51
+ Row_p p = ALLOC(Row);
52
+ p->view = view;
53
+ p->row = row;
54
+ return Data_Wrap_Struct(cRow, row_mark, 0, p);
55
+ }
56
+
57
+ ItemTypes AtRowCmd_OI (Item_p args) {
58
+ args[0].o = AtRow(args[0].o, args[1].i);
59
+ return IT_object;
60
+ }
61
+
62
+ static VALUE cCol;
63
+
64
+ static ItemTypes ListObjGetter (int row, Item_p item) {
65
+ VALUE ary = (VALUE) item->c.seq->data[0].p;
66
+ item->o = rb_ary_entry(ary, row);
67
+ return IT_object;
68
+ }
69
+
70
+ static void col_mark (Column_p p) {
71
+ if (p->seq->getter == ListObjGetter)
72
+ rb_gc_mark((VALUE) p->seq->data[0].p);
73
+ }
74
+
75
+ static void col_free_keep (Column_p p) {
76
+ PUSH_KEEP_REFS
77
+ DecRefCount(p->seq);
78
+ POP_KEEP_REFS
79
+ }
80
+
81
+ static VALUE cView;
82
+
83
+ static void view_free_keep (View_p p) {
84
+ PUSH_KEEP_REFS
85
+ DecRefCount(p);
86
+ POP_KEEP_REFS
87
+ }
88
+
89
+ static VALUE view_alloc (VALUE klass) {
90
+ return Data_Wrap_Struct(klass, 0, view_free_keep, 0);
91
+ }
92
+
93
+ VALUE ItemAsObj (ItemTypes type, Item_p item) {
94
+ VALUE result;
95
+
96
+ switch (type) {
97
+
98
+ case IT_int:
99
+ result = INT2NUM(item->i);
100
+ break;
101
+
102
+ case IT_wide:
103
+ result = NUM2OFFT(item->w);
104
+ break;
105
+
106
+ case IT_float:
107
+ result = NUM2DBL(item->f);
108
+ break;
109
+
110
+ case IT_double:
111
+ result = NUM2DBL(item->d);
112
+ break;
113
+
114
+ case IT_string:
115
+ result = rb_tainted_str_new(item->s, strlen(item->s));
116
+ break;
117
+
118
+ case IT_bytes:
119
+ result = rb_tainted_str_new((const char*) item->u.ptr, item->u.len);
120
+ break;
121
+
122
+ case IT_column: {
123
+ Column_p p;
124
+ result = Data_Make_Struct(cCol, Column, col_mark, col_free_keep, p);
125
+ *p = item->c;
126
+ IncRefCount(p->seq);
127
+ break;
128
+ }
129
+
130
+ case IT_view:
131
+ result = Data_Wrap_Struct(cView, 0, view_free_keep, IncRefCount(item->v));
132
+ break;
133
+
134
+ case IT_object:
135
+ result = item->o;
136
+ break;
137
+
138
+ default:
139
+ rb_raise (rb_eRuntimeError, "unsupported type %d", type);
140
+ }
141
+
142
+ return result;
143
+ }
144
+
145
+ static VALUE col_to_a_keep (VALUE self) {
146
+ int i, count;
147
+ Column_p column;
148
+ Item item;
149
+ VALUE result;
150
+
151
+ Data_Get_Struct(self, Column, column);
152
+ count = S_Count(column->seq);
153
+
154
+ result = rb_ary_new2(count);
155
+
156
+ PUSH_KEEP_REFS
157
+
158
+ for (i = 0; i < count; ++i) {
159
+ item.c = *column;
160
+ rb_ary_store(result, i, ItemAsObj(GetItem(i, &item), &item));
161
+ }
162
+
163
+ POP_KEEP_REFS
164
+ return result;
165
+ }
166
+
167
+ ItemTypes OpenCmd_S (Item_p args) {
168
+ args[0].v = OpenDataFile(args[0].s);
169
+ return IT_view;
170
+ }
171
+
172
+ int ObjToItem (ItemTypes type, Item_p item) {
173
+ switch (type) {
174
+
175
+ case IT_int:
176
+ item->i = NUM2INT(item->o);
177
+ break;
178
+
179
+ case IT_wide:
180
+ item->w = NUM2LL(item->o);
181
+ break;
182
+
183
+ case IT_float:
184
+ item->f = (float) NUM2DBL(item->o);
185
+ break;
186
+
187
+ case IT_double:
188
+ item->d = NUM2DBL(item->o);
189
+ break;
190
+
191
+ case IT_string:
192
+ item->s = STR2CSTR(item->o);
193
+ break;
194
+
195
+ case IT_bytes:
196
+ item->u.ptr = (const void*) rb_str2cstr(item->o, (long*) &item->u.len);
197
+ break;
198
+
199
+ case IT_object:
200
+ break;
201
+
202
+ case IT_column:
203
+ if (rb_class_of(item->o) != cCol)
204
+ return 0;
205
+ Data_Get_Struct(item->o, Column, item->p);
206
+ item->c = *(Column_p) item->p;
207
+ break;
208
+
209
+ case IT_view:
210
+ if (TYPE(item->o) == T_FIXNUM)
211
+ item->v = NoColumnView(NUM2INT(item->o));
212
+ if (rb_class_of(item->o) == cView)
213
+ Data_Get_Struct(item->o, Sequence, item->v);
214
+ else
215
+ return 0;
216
+ break;
217
+
218
+ default:
219
+ return 0;
220
+ }
221
+
222
+ return 1;
223
+ }
224
+
225
+ int ColumnByName (View_p meta, Object_p obj) {
226
+ int colnum;
227
+
228
+ if (TYPE(obj) == T_FIXNUM) {
229
+ colnum = NUM2INT(obj);
230
+ if (colnum < 0)
231
+ colnum += ViewSize(meta);
232
+ if (colnum < 0 || colnum >= ViewSize(meta))
233
+ return -5;
234
+ } else
235
+ colnum = StringLookup(STR2CSTR(obj), ViewCol(meta, MC_name));
236
+
237
+ return colnum;
238
+ }
239
+
240
+ Column ObjAsColumn (VALUE obj) {
241
+ int objc;
242
+ Object_p *objv;
243
+ Column result;
244
+
245
+ if (rb_class_of(obj) == cCol) {
246
+ Item item;
247
+ item.o = obj;
248
+ if (!ObjToItem(IT_column, &item))
249
+ Assert(0);
250
+ return item.c;
251
+ }
252
+
253
+ Assert(TYPE(obj) == T_ARRAY);
254
+ objc = (int) RARRAY(obj)->len;
255
+
256
+ result = InitColumn(objc, ListObjGetter, 0);
257
+ result.seq->data[0].p = (void*) obj;
258
+
259
+ result.pos = -1;
260
+ return result;
261
+ }
262
+
263
+ static VALUE view_initialize_keep (VALUE self, VALUE arg) {
264
+ View_p view;
265
+ Item item;
266
+ PUSH_KEEP_REFS
267
+
268
+ item.o = arg;
269
+ if (!ObjToItem(IT_view, &item))
270
+ Assert(0);
271
+
272
+ DATA_PTR(self) = IncRefCount(item.v);
273
+
274
+ POP_KEEP_REFS
275
+ return self;
276
+ }
277
+
278
+ #define MAX_STACK 20
279
+
280
+ static VALUE vq_dispatch_keep (int argc, VALUE *argv, VALUE self) {
281
+ int i, index;
282
+ VALUE result;
283
+ Item stack [MAX_STACK];
284
+ ItemTypes type;
285
+ const char *args;
286
+
287
+ index = NUM2INT(argv[0]);
288
+
289
+ PUSH_KEEP_REFS
290
+
291
+ --argc; ++argv;
292
+ args = f_commands[index].args + 2; /* skip return type and ':' */
293
+
294
+ for (i = 0; args[i] != 0; ++i) {
295
+ Assert(i < MAX_STACK);
296
+ if (args[i] == 'X') {
297
+ Assert(args[i+1] == 0);
298
+ stack[i].u.ptr = (const void*) (argv+i);
299
+ stack[i].u.len = argc-i;
300
+ break;
301
+ }
302
+ if ((args[i] == 0 && i != argc) || (args[i] != 0 && i >= argc))
303
+ rb_raise(rb_eRuntimeError, "wrong number of arguments");
304
+ stack[i].o = argv[i];
305
+ if (!CastObjToItem(args[i], stack+i))
306
+ rb_raise(rb_eRuntimeError, "%s: error in arg %d",
307
+ f_commands[index].name, i);
308
+ }
309
+
310
+ type = f_commands[index].proc(stack);
311
+ if (type == IT_unknown)
312
+ rb_raise(rb_eRuntimeError, "%s: error in result", f_commands[index].name);
313
+
314
+ result = ItemAsObj(type, stack);
315
+
316
+ POP_KEEP_REFS
317
+ return result;
318
+ }
319
+
320
+ static VALUE vq_cmdlist (VALUE self) {
321
+ int i, ncmds = sizeof f_commands / sizeof f_commands[0];
322
+ VALUE result = rb_ary_new2(ncmds);
323
+
324
+ for (i = 0; i < ncmds; ++i)
325
+ rb_ary_store(result, i, rb_str_new2(f_commands[i].name));
326
+
327
+ return result;
328
+ }
329
+
330
+ static VALUE view_each_keep (VALUE self) {
331
+ int r, rows;
332
+ Item item;
333
+
334
+ PUSH_KEEP_REFS
335
+
336
+ item.o = self;
337
+ if (!ObjToItem(IT_view, &item))
338
+ Assert(0);
339
+
340
+ rows = ViewSize(item.v);
341
+
342
+ for (r = 0; r < rows; ++r)
343
+ rb_yield(AtRow(self, r));
344
+
345
+ POP_KEEP_REFS
346
+ return self;
347
+ }
348
+
349
+ static VALUE row_aref_keep (VALUE self, VALUE index) {
350
+ int col;
351
+ Row_p p;
352
+ View_p v;
353
+ Item item;
354
+ VALUE result;
355
+
356
+ Data_Get_Struct(self, Row, p);
357
+ Data_Get_Struct(p->view, Sequence, v);
358
+
359
+ PUSH_KEEP_REFS
360
+
361
+ if (TYPE(index) == T_FIXNUM)
362
+ col = NUM2INT(index);
363
+ else
364
+ col = StringLookup(STR2CSTR(index), ViewCol(V_Meta(v), MC_name));
365
+
366
+ Assert(col >= 0);
367
+ item.c = ViewCol(v, col);
368
+ result = ItemAsObj(GetItem(p->row, &item), &item);
369
+
370
+ POP_KEEP_REFS
371
+ return result;
372
+ }
373
+
374
+ void Init_vlerq () {
375
+ VALUE vlerq = rb_define_module("Vlerq");
376
+ rb_define_module_function(vlerq, "cmdlist", vq_cmdlist, 0);
377
+ rb_define_module_function(vlerq, "dispatch", vq_dispatch_keep, -1);
378
+
379
+ cRow = rb_define_class_under(vlerq, "Row", rb_cObject);
380
+ rb_define_method(cRow, "_v", row_v, 0);
381
+ rb_define_method(cRow, "_i", row_i, 0);
382
+ rb_define_method(cRow, "[]", row_aref_keep, 1);
383
+
384
+ cCol = rb_define_class_under(vlerq, "Col", rb_cObject);
385
+ rb_define_method(cCol, "to_a", col_to_a_keep, 0);
386
+
387
+ cView = rb_define_class("View", rb_cObject);
388
+ rb_include_module(cView, rb_mEnumerable);
389
+
390
+ rb_define_alloc_func(cView, view_alloc);
391
+
392
+ rb_define_method(cView, "initialize", view_initialize_keep, 1);
393
+ rb_define_method(cView, "each", view_each_keep, 0);
394
+ }
data/ext/vlerq_ext.h ADDED
@@ -0,0 +1,197 @@
1
+ /* vlerq_ext.h - generated code, do not edit */
2
+
3
+ /* 73 definitions generated for Ruby:
4
+
5
+ # name in out inline-code
6
+ : drop U {} {}
7
+ : dup U UU {$1 = $0;}
8
+ : imul II I {$0.i *= $1.i;}
9
+ : nip UU U {$0 = $1;}
10
+ : over UU UUU {$2 = $0;}
11
+ : rot UUU UUU {$3 = $0; $0 = $1; $1 = $2; $2 = $3;}
12
+ : rrot UUU UUU {$3 = $2; $2 = $1; $1 = $0; $0 = $3;}
13
+ : swap UU UU {$2 = $0; $0 = $1; $1 = $2;}
14
+ : tuck UU UUU {$1 = $2; $0 = $1; $2 = $0;}
15
+
16
+ # name in out
17
+ : Data VX V
18
+ : HashCol SO C
19
+ : Max VN O
20
+ : Min VN O
21
+ : Open S V
22
+ : Set VIX V
23
+ : Sum VN O
24
+
25
+ tcl {
26
+ # name in out
27
+ : Def OO V
28
+ : Emit V O
29
+ : Get VX U
30
+ : Load O V
31
+ : Loop X O
32
+ : Mdef O V
33
+ : Ref OX O
34
+ : Refs O I
35
+ : Rename VO V
36
+ : Save VS I
37
+ : StructDesc V S
38
+ : Structure V S
39
+ : To OO O
40
+ : Type O O
41
+ : View X O
42
+ : Write VS I
43
+ }
44
+
45
+ ruby {
46
+ # name in out
47
+ : AtRow OI O
48
+ }
49
+
50
+ # name in out internal-name
51
+ : Blocked V V BlockedView
52
+ : Clone V V CloneView
53
+ : ColMap Vn V ColMapView
54
+ : ColOmit Vn V ColOmitView
55
+ : Coerce OS C CoerceCmd
56
+ : Compare VV I ViewCompare
57
+ : Compat VV I ViewCompat
58
+ : Concat VV V ConcatView
59
+ : CountsCol C C NewCountsColumn
60
+ : CountView I V NoColumnView
61
+ : First VI V FirstView
62
+ : GetCol VN C ViewCol
63
+ : Group VnS V GroupCol
64
+ : HashFind VIViii I HashDoFind
65
+ : Ijoin VV V IjoinView
66
+ : GetInfo VVI C GetHashInfo
67
+ : Grouped ViiS V GroupedView
68
+ : HashView V C HashValues
69
+ : IsectMap VV C IntersectMap
70
+ : Iota I C NewIotaColumn
71
+ : Join VVS V JoinView
72
+ : Last VI V LastView
73
+ : Mdesc S V DescToMeta
74
+ : Meta V V V_Meta
75
+ : NameCol V V NameColView
76
+ : OmitMap iI C OmitColumn
77
+ : Pair VV V PairView
78
+ : RemapSub ViII V RemapSubview
79
+ : Replace VIIV V ViewReplace
80
+ : RowEq IVIV I RowEqual
81
+ : RowHash VI I RowHash
82
+ : Size V I ViewSize
83
+ : SortMap V C SortMap
84
+ : Step VIIII V StepView
85
+ : StrLookup Ss I StringLookup
86
+ : Tag VS V TagView
87
+ : Take VI V TakeView
88
+ : Ungroup VN V UngroupView
89
+ : UniqueMap V C UniqMap
90
+ : ViewAsCol V C ViewAsCol
91
+ : Width V I ViewWidth
92
+
93
+ # name in out compound-definition
94
+ : Append VV V {over size swap insert}
95
+ : ColConv C C { }
96
+ : Counts VN C {getcol countscol}
97
+ : Delete VII V {0 countview replace}
98
+ : Except VV V {over swap exceptmap remap}
99
+ : ExceptMap VV C {over swap isectmap swap size omitmap}
100
+ : GroupInfo V C {dup 1 getinfo}
101
+ : HashInfo V C {dup 0 getinfo}
102
+ : Insert VIV V {0 swap replace}
103
+ : JoinInfo VV C {2 getinfo}
104
+ : Intersect VV V {over swap isectmap remap}
105
+ : Names V C {meta 0 getcol}
106
+ : Product VV V {over over size spread rrot swap size repeat pair}
107
+ : Repeat VI V {over size imul 0 1 1 step}
108
+ : Remap Vi V {0 -1 remapsub}
109
+ : Reverse V V {dup size -1 1 -1 step}
110
+ : Slice VIII V {rrot 1 swap step}
111
+ : Sort V V {dup sortmap remap}
112
+ : Spread VI V {over size 0 rot 1 step}
113
+ : Types V C {meta 1 getcol}
114
+ : Unique V V {dup uniquemap remap}
115
+ : Union VV V {over except concat}
116
+ : UnionMap VV C {swap exceptmap}
117
+ : ViewConv V V { }
118
+
119
+ # some operators are omitted from restricted execution environments
120
+ unsafe Open Save
121
+ */
122
+
123
+ { "append", "V:VV", AppendCmd_VV },
124
+ { "atrow", "O:OI", AtRowCmd_OI },
125
+ { "blocked", "V:V", BlockedCmd_V },
126
+ { "clone", "V:V", CloneCmd_V },
127
+ { "coerce", "C:OS", CoerceCmd_OS },
128
+ { "colconv", "C:C", ColConvCmd_C },
129
+ { "colmap", "V:Vn", ColMapCmd_Vn },
130
+ { "colomit", "V:Vn", ColOmitCmd_Vn },
131
+ { "compare", "I:VV", CompareCmd_VV },
132
+ { "compat", "I:VV", CompatCmd_VV },
133
+ { "concat", "V:VV", ConcatCmd_VV },
134
+ { "counts", "C:VN", CountsCmd_VN },
135
+ { "countscol", "C:C", CountsColCmd_C },
136
+ { "countview", "V:I", CountViewCmd_I },
137
+ { "data", "V:VX", DataCmd_VX },
138
+ { "delete", "V:VII", DeleteCmd_VII },
139
+ { "except", "V:VV", ExceptCmd_VV },
140
+ { "exceptmap", "C:VV", ExceptMapCmd_VV },
141
+ { "first", "V:VI", FirstCmd_VI },
142
+ { "getcol", "C:VN", GetColCmd_VN },
143
+ { "getinfo", "C:VVI", GetInfoCmd_VVI },
144
+ { "group", "V:VnS", GroupCmd_VnS },
145
+ { "grouped", "V:ViiS", GroupedCmd_ViiS },
146
+ { "groupinfo", "C:V", GroupInfoCmd_V },
147
+ { "hashcol", "C:SO", HashColCmd_SO },
148
+ { "hashfind", "I:VIViii", HashFindCmd_VIViii },
149
+ { "hashinfo", "C:V", HashInfoCmd_V },
150
+ { "hashview", "C:V", HashViewCmd_V },
151
+ { "ijoin", "V:VV", IjoinCmd_VV },
152
+ { "insert", "V:VIV", InsertCmd_VIV },
153
+ { "intersect", "V:VV", IntersectCmd_VV },
154
+ { "iota", "C:I", IotaCmd_I },
155
+ { "isectmap", "C:VV", IsectMapCmd_VV },
156
+ { "join", "V:VVS", JoinCmd_VVS },
157
+ { "joininfo", "C:VV", JoinInfoCmd_VV },
158
+ { "last", "V:VI", LastCmd_VI },
159
+ { "max", "O:VN", MaxCmd_VN },
160
+ { "mdesc", "V:S", MdescCmd_S },
161
+ { "meta", "V:V", MetaCmd_V },
162
+ { "min", "O:VN", MinCmd_VN },
163
+ { "namecol", "V:V", NameColCmd_V },
164
+ { "names", "C:V", NamesCmd_V },
165
+ { "omitmap", "C:iI", OmitMapCmd_iI },
166
+ { "open", "V:S", OpenCmd_S },
167
+ { "pair", "V:VV", PairCmd_VV },
168
+ { "product", "V:VV", ProductCmd_VV },
169
+ { "remap", "V:Vi", RemapCmd_Vi },
170
+ { "remapsub", "V:ViII", RemapSubCmd_ViII },
171
+ { "repeat", "V:VI", RepeatCmd_VI },
172
+ { "replace", "V:VIIV", ReplaceCmd_VIIV },
173
+ { "reverse", "V:V", ReverseCmd_V },
174
+ { "roweq", "I:IVIV", RowEqCmd_IVIV },
175
+ { "rowhash", "I:VI", RowHashCmd_VI },
176
+ { "set", "V:VIX", SetCmd_VIX },
177
+ { "size", "I:V", SizeCmd_V },
178
+ { "slice", "V:VIII", SliceCmd_VIII },
179
+ { "sort", "V:V", SortCmd_V },
180
+ { "sortmap", "C:V", SortMapCmd_V },
181
+ { "spread", "V:VI", SpreadCmd_VI },
182
+ { "step", "V:VIIII", StepCmd_VIIII },
183
+ { "strlookup", "I:Ss", StrLookupCmd_Ss },
184
+ { "sum", "O:VN", SumCmd_VN },
185
+ { "tag", "V:VS", TagCmd_VS },
186
+ { "take", "V:VI", TakeCmd_VI },
187
+ { "types", "C:V", TypesCmd_V },
188
+ { "ungroup", "V:VN", UngroupCmd_VN },
189
+ { "union", "V:VV", UnionCmd_VV },
190
+ { "unionmap", "C:VV", UnionMapCmd_VV },
191
+ { "unique", "V:V", UniqueCmd_V },
192
+ { "uniquemap", "C:V", UniqueMapCmd_V },
193
+ { "viewascol", "C:V", ViewAsColCmd_V },
194
+ { "viewconv", "V:V", ViewConvCmd_V },
195
+ { "width", "I:V", WidthCmd_V },
196
+
197
+ /* end of generated code */
data/lib/rural.rb ADDED
@@ -0,0 +1,65 @@
1
+ require 'vlerq'
2
+
3
+ module Rural
4
+ VERSION = '0.1.0'
5
+ end
6
+
7
+ module Vlerq
8
+ class Row
9
+ undef_method :type
10
+
11
+ def method_missing(m, *args)
12
+ self[m.to_s]
13
+ end
14
+ end
15
+ end
16
+
17
+ class View
18
+ undef_method :clone
19
+
20
+ Map = Hash.new { |h,k| fail "Unknown view operator: #{k}" }
21
+ Vlerq.cmdlist.each_with_index { |s,i| Map[s.to_sym] = i }
22
+
23
+ Map[:[]] = Map[:atrow]
24
+ Map[:length] = Map[:size]
25
+
26
+ def method_missing(m, *args)
27
+ Vlerq.dispatch(Map[m], self, *args)
28
+ end
29
+
30
+ def self.method_missing(m, *args)
31
+ Vlerq.dispatch(Map[m], *args)
32
+ end
33
+
34
+ def dump
35
+ out = Array.new(3 + size, '')
36
+
37
+ for cname, ctype in names.to_a.zip(types.to_a)
38
+ values = [cname, '-']
39
+
40
+ case ctype
41
+ when 'B'
42
+ each { |r| values << "#{r[cname].size}b" }
43
+ when 'V'
44
+ each { |r| values << "##{r[cname].size}" }
45
+ else
46
+ each { |r| values << r[cname].to_s }
47
+ end
48
+
49
+ widths = []
50
+ values.each { |s| widths << s.size }
51
+ w = [widths.max, 50].min
52
+ right = /[BILFDV]/ =~ ctype
53
+ fmt = " %#{right ? '' : '-'}#{w}.#{w}s"
54
+
55
+ values[0], values[1] = cname.ljust(w), ''.ljust(w, '-')
56
+ values << values[1]
57
+ values.each_with_index { |s,i| out[i] += fmt % s }
58
+ end
59
+ out.join "\n"
60
+ end
61
+
62
+ def project(*names)
63
+ colmap(names).unique
64
+ end
65
+ end