Skip to content

Commit 9076060

Browse files
committed
recipe: support (file) sets
recipe: set <name> <files> end (in target) (name) <files>
1 parent 254c72b commit 9076060

File tree

2 files changed

+196
-16
lines changed

2 files changed

+196
-16
lines changed

compiler/c2recipe_parser.c2

Lines changed: 189 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import csetjmp local;
2828
import stdarg local;
2929
import stdio local;
3030
import string;
31+
import stdlib;
3132

3233
type Kind enum u8 {
3334
// global
@@ -47,6 +48,8 @@ type Kind enum u8 {
4748
Export,
4849
Use,
4950
AsmFile,
51+
Set,
52+
SetUse,
5053
Eof,
5154
}
5255

@@ -67,6 +70,8 @@ const char*[] kind_names = {
6770
"$export",
6871
"$use",
6972
"$asm",
73+
"set",
74+
"(set)",
7075
"eof",
7176
}
7277

@@ -82,6 +87,53 @@ fn void Token.init(Token* t) {
8287
string.memset(t, 0, sizeof(Token));
8388
}
8489

90+
91+
type Set struct {
92+
u32 name_idx; // into aux pool
93+
SrcLoc loc;
94+
95+
build_target.File* files;
96+
u32 num_files;
97+
u32 max_files;
98+
99+
Set* next;
100+
}
101+
102+
fn Set* Set.create(u32 name, SrcLoc loc, Set* next) {
103+
Set* s = stdlib.calloc(1, sizeof(Set));
104+
s.name_idx = name;
105+
s.loc = loc;
106+
s.next = next;
107+
108+
s.max_files = 8;
109+
s.files = stdlib.malloc(8*sizeof(build_target.File));
110+
return s;
111+
}
112+
113+
fn void Set.free(Set* s) {
114+
stdlib.free(s.files);
115+
stdlib.free(s);
116+
}
117+
118+
fn bool Set.addFile(Set* s, u32 filename, SrcLoc loc) {
119+
for (u32 i=0; i<s.num_files; i++) {
120+
if (s.files[i].name == filename) return false;
121+
}
122+
123+
if (s.num_files == s.max_files) {
124+
s.max_files *= 2;
125+
build_target.File* files2 = stdlib.malloc(s.max_files * sizeof(build_target.File));
126+
string.memcpy(files2, s.files, s.num_files * sizeof(build_target.File));
127+
stdlib.free(s.files);
128+
s.files = files2;
129+
}
130+
131+
s.files[s.num_files].name = filename;
132+
s.files[s.num_files].loc = loc;
133+
s.num_files++;
134+
return true;
135+
}
136+
85137
type Parser struct {
86138
Recipe* recipe;
87139
string_pool.Pool* pool;
@@ -96,6 +148,17 @@ type Parser struct {
96148
bool new_line;
97149
bool targets_started;
98150
build_target.Target* target;
151+
Set* sets; // Note: will point to current set
152+
}
153+
154+
fn void Parser.free(Parser* p) {
155+
p.global_configs.free();
156+
Set* s = p.sets;
157+
while (s) {
158+
Set* next = s.next;
159+
s.free();
160+
s = next;
161+
}
99162
}
100163

101164
fn bool Parser.parse(Recipe* recipe, string_pool.Pool* pool, source_mgr.SourceMgr* sm, i32 file_id) {
@@ -117,7 +180,7 @@ fn bool Parser.parse(Recipe* recipe, string_pool.Pool* pool, source_mgr.SourceMg
117180
p.consumeToken();
118181
p.parseTop();
119182
}
120-
p.global_configs.free();
183+
p.free();
121184
return res == 0;
122185
}
123186

@@ -197,6 +260,10 @@ fn void Parser.lex(Parser* p, Token* result) {
197260
p.lex_option(result);
198261
p.new_line = false;
199262
return;
263+
case '(':
264+
p.lex_set_use(result);
265+
p.new_line = false;
266+
return;
200267
case '/':
201268
// filename
202269
// lex until whitespace
@@ -247,6 +314,12 @@ fn void Parser.lex(Parser* p, Token* result) {
247314
p.new_line = false;
248315
return;
249316
}
317+
if (equals(p.cur, "set ", 4)) {
318+
result.kind = Kind.Set;
319+
p.cur += 4;
320+
p.new_line = false;
321+
return;
322+
}
250323

251324
// lex until whitespace
252325
const char* start = p.cur;
@@ -286,8 +359,38 @@ fn void Parser.lex_plugin_options(Parser* p, Token* result) {
286359
}
287360
}
288361

289-
fn void Parser.lex_option(Parser* p, Token* result) {
362+
fn bool is_name(char c) {
363+
if (c >= 'a' && c <= 'z') return true;
364+
if (c >= 'A' && c <= 'Z') return true;
365+
if (c >= '0' && c <= '9') return true;
366+
if (c == '_') return true;
367+
return false;
368+
}
369+
370+
fn void Parser.lex_set_use(Parser* p, Token* result) {
371+
p.cur++; // skip (
372+
373+
result.loc = p.loc_start + cast<SrcLoc>(p.cur - p.input_start);
374+
375+
const char* start = p.cur;
376+
while (is_name(*p.cur)) p.cur++;
377+
378+
result.kind = Kind.SetUse;
379+
u32 len = cast<u32>(p.cur - start);
380+
if (len == 0) p.error("expected set name");
381+
382+
result.value = p.pool.add(start, len, true);
383+
384+
if (*p.cur != ')') {
385+
result.loc = p.loc_start + cast<SrcLoc>(p.cur - p.input_start);
386+
p.error("expected ')'");
387+
}
290388
p.cur++;
389+
}
390+
391+
392+
fn void Parser.lex_option(Parser* p, Token* result) {
393+
p.cur++; // skip $
291394

292395
result.loc = p.loc_start + cast<SrcLoc>(p.cur - p.input_start);
293396

@@ -387,6 +490,12 @@ fn void Parser.parseTop(Parser* p) {
387490
case AsmFile:
388491
p.error("must be inside target");
389492
break;
493+
case Set:
494+
p.parseSet();
495+
break;
496+
case SetUse:
497+
p.error("syntax error");
498+
break;
390499
case Eof:
391500
return;
392501
}
@@ -419,7 +528,8 @@ fn void Parser.parseWarnings(Parser* p) {
419528
// TODO no need to store in string pool
420529
while (p.is(Kind.Text)) {
421530
const char* option = p.pool.idx2str(p.token.value);
422-
if (string.strcmp(option, "no-unused") == 0) {
531+
switch (option) {
532+
case "no-unused":
423533
warnings.no_unused = true;
424534
warnings.no_unused_variable = true;
425535
warnings.no_unused_function = true;
@@ -430,28 +540,40 @@ fn void Parser.parseWarnings(Parser* p) {
430540
warnings.no_unused_public = true;
431541
warnings.no_unused_label = true;
432542
warnings.no_unused_enum_constant = true;
433-
} else if (string.strcmp(option, "no-unused-variable") == 0) {
543+
break;
544+
case "no-unused-variable":
434545
warnings.no_unused_variable = true;
435-
} else if (string.strcmp(option, "no-unused-function") == 0) {
546+
break;
547+
case "no-unused-function":
436548
warnings.no_unused_function = true;
437-
} else if (string.strcmp(option, "no-unused-parameter") == 0) {
549+
break;
550+
case "no-unused-parameter":
438551
warnings.no_unused_parameter = true;
439-
} else if (string.strcmp(option, "no-unused-type") == 0) {
552+
break;
553+
case "no-unused-type":
440554
warnings.no_unused_type = true;
441-
} else if (string.strcmp(option, "no-unused-module") == 0) {
555+
break;
556+
case "no-unused-module":
442557
warnings.no_unused_module = true;
443-
} else if (string.strcmp(option, "no-unused-import") == 0) {
558+
break;
559+
case "no-unused-import":
444560
warnings.no_unused_import = true;
445-
} else if (string.strcmp(option, "no-unused-public") == 0) {
561+
break;
562+
case "no-unused-public":
446563
warnings.no_unused_public = true;
447-
} else if (string.strcmp(option, "no-unused-label") == 0) {
564+
break;
565+
case "no-unused-label":
448566
warnings.no_unused_label = true;
449-
} else if (string.strcmp(option, "no-unused-enum-constant") == 0) {
567+
break;
568+
case "no-unused-enum-constant":
450569
warnings.no_unused_enum_constant = true;
451-
} else if (string.strcmp(option, "promote-to-error") == 0) {
570+
break;
571+
case "promote-to-error":
452572
warnings.are_errors = true;
453-
} else {
573+
break;
574+
default:
454575
p.error("unknown warning '%s'", option);
576+
break;
455577
}
456578
p.consumeToken();
457579
}
@@ -475,6 +597,44 @@ fn void Parser.parseImage(Parser* p) {
475597
p.parseTarget();
476598
}
477599

600+
fn void Parser.parseSet(Parser* p) {
601+
p.consumeToken();
602+
p.expect(Kind.Text, "expect set name");
603+
604+
Set* old = p.findSet(p.token.value);
605+
if (old) {
606+
p.error("duplicate set '%s'", p.pool.idx2str(p.token.value));
607+
}
608+
p.sets = Set.create(p.token.value, p.token.loc, p.sets);
609+
p.consumeToken();
610+
611+
while (1) {
612+
switch (p.token.kind) {
613+
case File:
614+
if (!p.sets.addFile(p.token.value, p.token.loc)) {
615+
p.error("duplicate file '%s' in set '%s'", p.pool.idx2str(p.token.value), p.pool.idx2str(p.sets.name_idx));
616+
}
617+
p.consumeToken();
618+
break;
619+
case End:
620+
p.consumeToken();
621+
return;
622+
default:
623+
p.error("syntax error");
624+
break;
625+
}
626+
}
627+
}
628+
629+
fn Set* Parser.findSet(Parser* p, u32 name_idx) {
630+
Set* s = p.sets;
631+
while (s) {
632+
if (s.name_idx == name_idx) return s;
633+
s = s.next;
634+
}
635+
return nil;
636+
}
637+
478638
fn void Parser.parseLibrary(Parser* p) {
479639
p.consumeToken();
480640
p.expect(Kind.Text, "expect target name");
@@ -609,6 +769,21 @@ fn void Parser.parseTarget(Parser* p) {
609769
}
610770
p.consumeToken();
611771
break;
772+
case Set:
773+
p.error("cannot define a set here");
774+
break;
775+
case SetUse:
776+
Set* s = p.findSet(p.token.value);
777+
if (!s) p.error("unknown set '%s'", p.pool.idx2str(p.token.value));
778+
p.consumeToken();
779+
files_started = true;
780+
for (u32 i=0; i<s.num_files; i++) {
781+
const build_target.File* f = &s.files[i];
782+
if (!p.target.addFile(f.name, f.loc)) {
783+
p.error("duplicate file '%s' from set '%s'", p.pool.idx2str(f.name), p.pool.idx2str(s.name_idx));
784+
}
785+
}
786+
break;
612787
case Eof:
613788
p.error("un-terminated target");
614789
return;

compiler/main.c2

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ const char[] Recipe_help =
105105
"\n"
106106
"config <options>\n"
107107
"\n"
108+
"set <name>\n"
109+
" <files>\n"
110+
"end\n"
111+
"\n"
108112
"executable <name>\n"
109113
" $warnings <no-unused>\n"
110114
" <no-unused-variable>\n"
@@ -125,8 +129,9 @@ const char[] Recipe_help =
125129
" $config <options>\n"
126130
" $plugin <name> [<plugin-options>]\n"
127131
" $use <library-name> dynamic/static\n"
128-
" file1.c2\n"
129-
" file2.c2\n"
132+
" (set-name)\n"
133+
" <file1.c2>\n"
134+
" <file2.c2>\n"
130135
"end\n"
131136
"\n"
132137
"lib <name> dynamic/static\n"

0 commit comments

Comments
 (0)