export cstr types vec use vio brace(vec *lines) ssize_t lineno = 0 brace_init() if readstmt(lines, lineno++) writestmt() while readstmt(lines, lineno++) writedelim() writestmt() writedelim() def MAXTABS 256 # TODO local enum, struct, etc. enum { SWITCH, WHICH, STRUCT, CLASS, INIT, VOID_MAIN, MACRO, DO, DOWHILE, ELSE, OTHER } local int blocktype[MAXTABS] local char *kwdparens[] = { "if", "else if", "while", "do", "for", "switch", "else", 0 } local char *l local int len local int tabs local int lasttabs local int skipsemi local char *label local char *lastlabel # XXX do lastlabel and lastcase work? # isn't the buffer overwritten for each line? local int lastblank local char *caselabel local char *lastcase local int casetabs local int in_macro local int first_line_of_macro local int is_kwdparens local int is_static local brace_init() lastlabel = 0 lastcase = 0 in_macro = 0 first_line_of_macro = 0 local int readstmt(vec *lines, ssize_t lineno) if !readln(lines, lineno) tabs = 0 return 0 tabs = striptabs() strip_one_space_maybe() fussy() label = 0 if len > 0 && tabs == 0 int lbllen = wordlen() if l[lbllen] == '\t' label = l l += lbllen len -= lbllen tabs = striptabs() label[lbllen] = '\0' caselabel = 0 casetabs = 0 if len > 0 && tabs > 0 && l[0] != '#' int lbllen = caselen() if l[lbllen] == ',' && l[lbllen+1] == '\t' l[lbllen] = '\0' lbllen++ if l[lbllen] == '\t' caselabel = l l += lbllen len -= lbllen casetabs = striptabs() caselabel[lbllen] = '\0' tabs += casetabs if len == 1 && l[0] == '.' l[0] = '\0' ; len = 0 if tabs >= MAXTABS error("too many tabs") return 1 local writestmt() is_kwdparens = 0 is_static = 0 if caselabel && lasttabs >= tabs && !(lastblank && lastcase) \ && blocktype[tabs-1] == WHICH indent(tabs) print("break;\n") if label if isdigit((int)label[0]) print("_") print(label) print(":") indent(tabs - casetabs) if caselabel if caselabel[0] == '\0' error("spurious space between tabs") eif strcmp(caselabel, "else") == 0 print("default:") else print("case ") print(caselabel) print(":") indent(casetabs) if strcmp(l, "do") == 0 blocktype[tabs] = DO eif blocktype[tabs] == DO && cstr_begins_with(l, "while ") blocktype[tabs] = DOWHILE eif cstr_begins_with(l, "switch ") blocktype[tabs] = SWITCH eif cstr_begins_with(l, "else") && (len == 4 || l[4] == ' ') blocktype[tabs] = ELSE eif cstr_begins_with(l, "which ") blocktype[tabs] = WHICH eif (cstr_begins_with(l, "enum") && (len == 4 || l[4] == ' ')) || \ l[len-1] == '=' || \ (tabs > 0 && blocktype[tabs-1] == INIT) blocktype[tabs] = INIT eif tabs > lasttabs && blocktype[lasttabs] == INIT int i for i=lasttabs+1; i<=tabs; ++i blocktype[i] = INIT else # check for struct, union, class char *c = l if cstr_begins_with(c, "template<") c = strchr(c+9, '>') if c == NULL error("template is missing >") ++c if *c == '\0' error("template<...> must be followed by the start of a declaration on the same line") if *c != ' ' error("template<...> must be followed by a space") ++c eif cstr_begins_with(c, "extern \"C\" ") c += 11 if *c == '\0' error("extern \"C\" must be followed by the start of a declaration on the same line") if cstr_begins_with(c, "struct ") && classy(c+7) blocktype[tabs] = STRUCT eif cstr_begins_with(c, "union ") && classy(c+6) blocktype[tabs] = STRUCT eif cstr_begins_with(c, "class ") && classy(c+6) blocktype[tabs] = CLASS eif *c == '^' || *c == '#' # ignore directives and comments, not indented properly else blocktype[tabs] = OTHER # to be continued? skipsemi = 0 if len > 0 && l[wordlen()] == '\0' && tabs > 0 && \ blocktype[tabs] != INIT && \ strcmp(l, "else") != 0 && \ strcmp(l, "return") != 0 && \ strcmp(l, "break") != 0 && \ strcmp(l, "continue") != 0 && \ strcmp(l, "do") != 0 && \ strcmp(l, "repeat") != 0 print("goto ") if isdigit((int)l[0]) print("_") eif l[0] == '#' print("/") l[0] = '/' eif cstr_begins_with(l, "export ") || cstr_begins_with(l, "use ") l = strchr(l, ' ') + 1 print("#include ") print(l) l = "" ; len = 0 eif cstr_begins_with(l, "def ") if tabs != 0 error("macro definitions must be at top level") blocktype[tabs] = MACRO in_macro = 1 first_line_of_macro = 1 print("#define ") l += 4 ; len -= 4 eif cstr_begins_with(l, "local ") || cstr_begins_with(l, "static ") char *l2 = strchr(l, ' ')+1 print("static ") is_static = 1 len -= (l2-l) ; l = l2 if tabs == 0 addvoids() eif cstr_begins_with(l, "^") print("#") skipsemi = 1 l++ ; len-- eif l[len-1] == '{' || strcmp(l, "}") == 0 skipsemi = 1 eif tabs == 0 addvoids() else procstmt() print(l) skipsemi = skipsemi || len == 0 || l[0] == '"' || l[0] == '<' || \ l[0] == '/' || last() == '/' if caselabel && len == 0 skipsemi = 0 lastblank = len == 0 lastlabel = 0 lastcase = 0 if label || !lastblank lastlabel = label if caselabel && !lastblank lastcase = caselabel lasttabs = tabs local writedelim() int lt = lasttabs if first_line_of_macro skipsemi = 1 if tabs != 0 lt = tabs if lt >= tabs && \ (!skipsemi || \ (lastblank && (lastlabel || lastcase))) && \ !(lt > 0 && blocktype[lt-1] == INIT) if is_kwdparens && blocktype[lt] != DOWHILE print(" {}") else print(";") if in_macro && tabs > 0 print(" \\") print("\n") if !(in_macro && tabs == 0) while lt > tabs indent(--lt) if blocktype[lt] == STRUCT || blocktype[lt] == CLASS print("};\n") eif blocktype[lt] == INIT && !(lt>0 && blocktype[lt-1] == INIT) print("};\n") else if lt == 0 && blocktype[0] == VOID_MAIN print("\treturn 0;\n") print("}\n") while lt < tabs indent(lt++) print("{\n") if in_macro && tabs == 0 in_macro = 0 first_line_of_macro = 0 local procstmt() char **k char *c if (c = cstr_begins_with(l, "which ")) print("switch(") print(c) l = ")" ; len = 1 eif cstr_begins_with(l, "eif ") print("else if(") print(l+4) l = ")" ; len = 1 is_kwdparens = 1 eif strcmp(l, "repeat") == 0 l = "while(1)" ; len = 8 #eif strcmp(l, "stop") == 0 # l = "break" ; len = 5 else for k=kwdparens; *k != 0; ++k int c = strlen(*k) if cstr_begins_with(l, *k) && (l[c] == ' ' || l[c] == '\0') if l[c] == ' ' l[c] = '(' print(l) l = ")" ; len = 1 is_kwdparens = 1 break local int readln(vec *lines, ssize_t lineno) if lineno >= veclen(lines) return 0 l = *(cstr*)v(lines, lineno) len = strlen(l) # while last() == '\n' || last() == '\r' # l[--len] = '\0' return 1 local char last() if len == 0 return '\0' return l[len-1] local int striptabs() int tabs = 0 while l[0] == '\t' ++l ++tabs --len return tabs local fussy() if l[0] == ' ' error("two spaces at start of line") if last() == ' ' error("space at end of line") local int wordlen() return strspn(l, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789.") local int caselen() return strcspn(l, "\t") local strip_one_space_maybe() if l[0] == ' ' ++l --len local indent(int tabs) for ; tabs>0; --tabs print("\t") local addvoids() char *c1 = cstr_begins_with(l, "extern \"C\" ") if c1 print("extern \"C\" ") l = c1 int addvoid = 1 char *c = l for ; *c != 0; ++c if *c == ' ' addvoid = 0 if *c == '(' if addvoid if cstr_begins_with(l, "main(") print("int ") blocktype[tabs] = VOID_MAIN else print("void ") if c[1] == ')' c[1] = '\0' print(l) c[1] = ')' print("void") len -= (c - l) + 1 l = c + 1 break local int classy(char *c) char *spc = strchr(c, ' ') char *colon = strchr(c, ':') char *paren = strchr(c, '(') return !paren && (!spc || colon == spc + 1 || colon < spc) macrospace *builtins use b_plain char use_b[] = "use b" char add_main[] = "Main:" boolean cz_translate_compile() boolean ok = 1 if !opt("clean") clean() FILE *log = Fopen(log_file, "w+") stdio_redirect(stderr, log) try(x) translate() if !opt("translate") compile() except(x) warn_errors() ok = 0 Fflush(stderr) if file_size(log_file) rewind(log) fcp(log, stderr) Fclose(log) return ok translate() boolean ok = 1 tofree_block() try(x) vecclr(lines) ; vecclr(work) if opt("useb") vec_push_cstr(lines, use_b) if opt("addmain") vec_push_cstr(lines, add_main) src_file_data = read_lines(lines, src_file) if opt("addmain") for_vec(i, lines, cstr) cstr l = *i if l == use_b || l == add_main: continue cstr t = Malloc(strlen(l)+1) *t = '\t' strcpy(t+1, l) *i = t if cz_line_numbers number_lines(src_file) b2b1() if opt("b1file") write_lines(lines, b1_file) F_out(c_file) brace(lines) except(x) ok = 0 Free(src_file_data) if !ok Throw() b2b1() b2b0() b02b1() b2b0() rm_comments_blank() contlines() split_semicolons() cz_parse() split_use() split_vars() b02b1() cz_includes() # cz_meta is disabled for now, as it can't work yet # cz_meta() cz_expand_macros() cz_processes() cz_wrappers() if cz_standalone cz_strip() cz_pc() cz_protos() cz_typedefs() cz_uniq() cz_shuffle() # ok = !Systemlu("<", q(src_file), "brace_number_lines", "|", "b2b1", ">", q(b1_file)) boolean cz_create_header() boolean ok = 1 tofree_block() try(x) vecclr(lines) ; vecclr(work) if opt("useb") vec_push_cstr(lines, use_b) if opt("addmain") vec_push_cstr(lines, add_main) src_file_data = read_lines(lines, src_file) if opt("addmain") for_vec(i, lines, cstr) cstr l = *i if l == use_b || l == add_main: continue cstr t = Malloc(strlen(l)+1) *t = '\t' strcpy(t+1, l) *i = t if cz_line_numbers number_lines(src_file) b2bh() write_lines(lines, bh_file) except(x) warn_errors() ok = 0 Free(src_file_data) return ok b2bh() b2b0() b02bh() b02bh() # is this right? several things would be done twice when it's included? if cz_bh_no_macros vecclr(defines) # cz_processes() # cz_wrappers() cz_protos() cz_header() cz_uniq() cz_shuffle() compile() int rv cstr cc = cz_lang == LANG_CXX ? cz_cxx : cz_cc new(cc_args, vec, cstr, 16) vec_append(cc_args, cz_lang == LANG_CXX ? cxxflags : cflags) vec_append(cc_args, ldflags) cstr o_exe = Format("-o%s", exe_file) vec_push_cstr(cc_args, o_exe) vec_push_cstr(cc_args, c_file) vec_push_cstr(cc_args, cz_L_so) vec_append(cc_args, ldlibs) rv = !Systema(cc, vec_to_array(cc_args)) vec_free(cc_args) Free(o_exe) if !rv error("compile failed") clean() each(x, c_file, b1_file, exe_file, log_file) remove(x) if opt("nobackup") each(x, xbak_file, bbak_file, bbak2_file) remove(x) use b_plain contlines() new(b, buffer, line_initial_space) int bslashcont = -1 int indent int prev_indent = 0 cstr prev_line = "" vec_push(lines, (cstr)"") for_vec(i, lines, cstr) cstr l = *i indent = line_indent(l) char *after_space = l if bslashcont == 1 l += strspn(l, " \t") eif (l[0] == ' ' || (after_space = strstr(l, "\t "))) && indent >= prev_indent && !cstr_begins_with_word(after_space, "else") && !cstr_begins_with_word(after_space, "eif") && !cstr_begins_with_word(after_space, "while") l += prev_indent eif bslashcont == 0 if buflen(b) == 0 vec_push(work, prev_line) else buffer_cat_cstr(b, prev_line) vec_push(work, tofree(buffer_to_cstr(b))) init(b, buffer, line_initial_space) prev_line = "" prev_indent = indent int len = strlen(l) if len && l[len-1] == '\\' l[len-1] = '\0' bslashcont = 1 else bslashcont = 0 if *prev_line buffer_cat_cstr(b, prev_line) prev_line = l if *prev_line error("last line ends in \\ - how perverse!") swap(lines, work) vecclr(work) local long private_count cstr private_prefix cstr private_prefix_up # subroutines / nested functions would be nice for this # TODO do this private_prefix stuff as a builtin, so can use a single buffer for it #def debug_macros warn def debug_macros void cstr new_private_prefix() static buffer struct__b static buffer *b = NULL if !b # 32 chars is more than long enough that it won't be realloc'd b = &struct__b init(b, buffer, 32) buffer_cat_cstr(b, "my__") buffer_set_size(b, 4) buffer_cat_long(b, private_count++) buffer_cat_char(b, '_') return buffer_add_nul(b) cz_expand_macros() New(global, macrospace, builtins) load_macros(global) cz_expand_macros__private_count_init(global) expand_macros(global) cz_expand_macros__private_count_init(macrospace *global): private_count = 0 private_prefix = new_private_prefix() Decl(private_prefix_key, macro_key) private_prefix_key->name = "my__prefix" private_prefix_key->n_args = -1 New(private_def_lines, vec, cstr, 1) vec_push(private_def_lines, private_prefix) New(private_def, macro_def, private_prefix_key->name, NULL, private_def_lines, 0, 0, 0) macrospace_set(global, private_prefix_key, private_def) private_prefix_up = Malloc(32) *private_prefix_up = '\0' Decl(private_prefix_up_key, macro_key) private_prefix_up_key->name = "my__prefix_up" private_prefix_up_key->n_args = -1 New(private_up_def_lines, vec, cstr, 1) vec_push(private_up_def_lines, private_prefix_up) New(private_up_def, macro_def, private_prefix_up_key->name, NULL, private_up_def_lines, 0, 0, 0) macrospace_set(global, private_prefix_up_key, private_up_def) expand_macros(macrospace *ms) # NOTE cz macro processor does not currently apply macros to ALL parts of the source, only the following: each(vov, structs_unions_classes_templates, enums, typedefs, functions, var_assignments, local_and_global_vars) for_vec(lines, vov, vec*) expand_macros_in_lines_inplace(*lines, ms) new(line1, vec, cstr, 1) vec_set_size(line1, 1) each(vos, struct_union_class_template_protos, struct_union_typedefs, function_protos, var_protos) for_vec(line, vos, cstr) *(cstr*)vec0(line1) = *line expand_macros_in_lines_inplace(line1, ms) let(l, veclen(line1)) if l != 1 # it expanded to zero or more than one line, we need to parse this again :( debug_macros("cz_expand_macros: macro expanded to %d lines in single-line context: %s", l, *(cstr*)line) swap(lines, line1) cz_parse() swap(lines, line1) *line = NULL vec_set_size(line1, 1) else: *line = *(cstr*)vec0(line1) remove_null(vos) vec_free(line1) # TODO reparse() like in perl version? is that needed? load_macros(macrospace *ms) for_vec(d, defines, vec*) cstr name, args, definition vec *params = NULL boolean block = 1, ldef = 0 int raw = 0 cstr line = *(cstr*)vec0(*d) int lines = veclen(*d) if line[0] == '^' continue cstr l0 = tofree(Strdup(line)) delimit(name, l0, ' ') else invalid_macro_def("missing name", line) cstr keyword = l0 if (ldef = *keyword == 'l') ++keyword if cstr_eq(keyword, "def") raw = 0 eif cstr_eq(keyword, "Def") raw = 1 eif cstr_eq(keyword, "DEF") raw = 2 else invalid_macro_def("keyword", line) # this is a bit ugly # how to do proper parsing nicely? char *space = strchr(name, ' ') char *paren = strchr(name, '(') if paren && (!space || paren < space) delimit(args, paren, '(') delimit(definition, args, ')') if *definition == ' ' ++definition block = 0 eif *definition invalid_macro_def("character after )", line) NEW(params, vec, cstr, 32) splitv(params, args, ',') for_vec(arg, params, cstr) while **arg == ' ' ++*arg else invalid_macro_def("missing )", line) eif space delimit(definition, space, ' ') block = 0 if block == 0 && lines != 1 invalid_macro_def("expression / block macro confusion", line) if block vec_shift(*d) else *(cstr*)vec0(*d) = definition New(m, macro_def, name, params, *d, block, ldef, raw) int n_args = params ? veclen(params) : -1 macro_key mk = { name, n_args } debug_macros("new macro: %s %d", mk.name, mk.n_args) macrospace_set(ms, &mk, m) # TODO use let later when we do macros in order... *d = NULL remove_null(defines) # macrospace_dump(ms) invalid_macro_def(cstr desc, cstr line) error("invalid macro definition, %s: %s", desc, line) expand_macros_in_lines_inplace(vec *lines_v, macrospace *ms) New(lines_out, vec, cstr, veclen(lines_v)*2) expand_macros_in_lines(lines_out, lines_v, ms) *lines_v = *lines_out vec_squeeze(lines_v) expand_macros_in_lines(vec *lines_out, vec *lines_v, macrospace *ms) # The working state for expanding macros is a little complex. The # initial input is a vector of lines, and the final output is a vector # of lines. The output of a single macro expansion may be part of a # line, or one or more lines. In either case, this output must go # through the macro processor again. We need to somehow prepend the # output to the input queue. I do want this to run fast, but I'm not # overly concerned about efficiency at this stage, I think it will be # fast enough. So I will simply convert the input vector to a deq, # accumulate output in a new vec, and then prepend that vec to the # existing input deq. For in-line macros, I will need to prepend the # output to the current line. The output can remain in a buffer, so I # can just append the rest of the current line to it. # I would like to be able to detect (deeply) recursive macros too. decl(lines, deq) vec_to_deq(lines, lines_v) while deqlen(lines): deq_shift(lines, l) expand_macros_in_line(lines_out, lines, l, ms) #def begins cstr_begins_with #def word cstr_begins_with_word vec_copy(vec *to, vec *from) vecclr(to) vec_append_vec(to, from) vec_append_vec(vec *to, vec *from) vec_append(to, vec0(from), veclen(from)) expand_macros_in_line(vec *lines_out, deq *lines, cstr l, macrospace *ms) debug_macros("expand_macros_in_line: %s", l) int indent = 0 char *paste = NULL boolean changed = 0 boolean block = 0 char *p = l vec struct__args vec *args = NULL buffer struct__b buffer *b = &struct__b char *tok_prev = NULL while *p char *tok = p macro_def *m = NULL token_t tt = token(&p) if tt == TABS: indent += p - tok eif tt == NAME: expand_macros_in_line__name_token() eif *tok == '^' && tok[1] == '^' && p-tok == 2: # token pasting operator if !tok_prev: error("pasting operator ^^ found at start of line - nothing to paste") if !changed: init(b, buffer, 256) buffer_cat_range(b, l, tok) changed = 1 paste = tok_prev m = (macro_def*)1 # token pasting is somewhat 'like' macro expansion if changed && !m && !paste: buffer_cat_range(b, tok, p) eif changed && !m && paste: debug_macros("prepaste: b = %s len = %d", buffer_nul_terminate(b)) new(b1, buffer, 256) expand_macros_in_line__paste(b1, paste, tok) p = l = tofree(buffer_to_cstr(b1)) debug_macros("pasted: b = %s paste = %s tok = %s p = %s", buffer_nul_terminate(b), paste, tok, p) paste = NULL tok_prev = tok if !block: if changed: vec_push(lines_out, tofree(buffer_to_cstr(b))) # TODO ? else: vec_push(lines_out, l) def expand_macros_in_line__paste(b1, paste, tok): char *tok0 = paste token(&paste) int tok0_len = paste - tok0 buffer_grow(b, -tok0_len) boolean tok0_string = paste[-1] == '"' boolean tok1_string = *tok == '"' if tok1_string && !tok0_string: buffer_cat_char(b1, '"') buffer_cat_range(b1, tok0, paste) if tok0_string: buffer_grow(b1, -1) if tok1_string: ++tok char *tok1 = tok token(&tok) buffer_cat_range(b1, tok1, tok) if tok0_string && !tok1_string: buffer_cat_char(b1, '"') buffer_cat_cstr(b1, tok) def expand_macros_in_line__name_token(): Tmpnul(p, c) debug_macros("name token: %s", tok) if macrospace_lookup_vague(ms, tok) expand_macros_in_line__found_vague() if m if m->block: if changed: if b: l = tofree(buffer_to_cstr(b)) b = NULL tok = l + strlen(l) else: changed = 1 block = 1 boolean at_start = tok == l || tok[-1] == '\t' || (tok - l >= 2 && tok[-1] == ' ' && tok[-2] == '\t') if *p || !at_start: error("cz_expand_macros: cannot expand block macro '%s' in the middle of a line: %s", m->name, l) apply_block_macro(lines, l, tok, m, args, ms, indent) else: if !changed: init(b, buffer, 256) buffer_cat_range(b, l, tok) changed = 1 boolean wrap = !(buffer_last_char(b) == '(' && *p == ')') apply_inline_macro(&l, &p, tok, m, args, ms, wrap) def expand_macros_in_line__found_vague(): debug_macros("found vague!") # look for a macro without args first macro_key mk = { tok, -1 } m = namespace_lookup(&ms->ns, &mk) if m: debug_macros(" found exact, without args!") if args: vec_clear(args) eif c == '(': # get the arguments ++p if !args: args = &struct__args vec_init(args, macro_arg, 20) else: vec_clear(args) char *p1 = get_macro_args(p, args, tok, ms) mk.n_args = veclen(args) m = namespace_lookup(&ms->ns, &mk) if m: debug_macros(" found exact, with %d args!", mk.n_args) p = p1 debug_macros(" p is now: %s", p) else: debug_macros(" putting args back - just replace \\0 chars with , , , )") debug_macros(" p: %s", p) debug_macros(" p1: %s", p1) char *fix for fix = p; fix != p1; ++fix: if !*fix: *fix = ',' p1[-1] = ')' debug_macros(" fixed: %s", p) # unfortunately applying / expanding macros with this data structure is quite tricky apply_inline_macro(cstr *l, char **after, char *before, macro_def *m, vec *args, macrospace *ms, boolean wrap): debug_macros(" apply_inline_macro %s", m->name) debug_macros(" after: %s", *after) cstr repl = *(cstr*)vec0(m->lines) # substitute args; aargh! new(b, buffer, 256) substitute_args_in_range(b, repl, repl+strlen(repl), m, args) new(expanded, vec, cstr, 1) vec_push(expanded, tofree(buffer_to_cstr(b))) expand_macros_in_lines_inplace_with_new_private_prefix(expanded, ms) cstr line = *(cstr*)vec0(expanded) long tok_count = count_tokens(line) wrap = wrap && !m->raw && tok_count > 1 cstr expansion = *(cstr*)vec0(expanded) char last_char = cstr_last_char(expansion) if among(last_char, '*', '&'): wrap = 0 new(b1, buffer, 256) if wrap: buffer_cat_char(b1, '(') buffer_cat_cstr(b1, expansion) if wrap: buffer_cat_char(b1, ')') buffer_cat_cstr(b1, *after) *l = tofree(buffer_to_cstr(b1)) *after = *l debug_macros(" rest: %s", *l) use(before) ; use(args) ; use(ms) long count_tokens(cstr s) long c = 0 while token(&s) != TOK_EOT ++c return c # this is junky but I'm getting desperate to get this all working! expand_macros_in_lines_inplace_with_new_private_prefix(vec *lines, macrospace *ms) char old_prefix[32] strcpy(old_prefix, private_prefix_up) strcpy(private_prefix_up, private_prefix) private_prefix = new_private_prefix() expand_macros_in_lines_inplace(lines, ms) strcpy(private_prefix, private_prefix_up) strcpy(private_prefix_up, old_prefix) apply_block_macro(deq *lines, cstr l, char *before, macro_def *m, vec *args, macrospace *ms, int indent): new(v, vec, cstr, 256) boolean first = 1 debug_macros(" apply_block_macro %s", m->name) debug_macros(" l: %s", l) debug_macros(" before: %s", before) # TODO use l for something? for_vec(xp, m->lines, cstr) cstr x = *xp # TODO args, is indent right? new(b, buffer, 256) int extra_tabs = indent - 1 if first: buffer_cat_range(b, l, before) extra_tabs = -1 first = 0 char *tab = *x == '\t' ? x : strchr(x, '\t') if !tab: error("cz macro line is missing any tab for indent!") if tab != x: substitute_args_in_range(b, x, tab, m, args) x = tab if extra_tabs >= 0: repeat(extra_tabs): # TODO make more efficient ala single grow + memset? buffer_cat_char(b, '\t') eif extra_tabs == -1: ++x else: # extra_tabs < -1 fault("apply_block_macro: attempt to shift left by more than 1 tab") substitute_args_in_range(b, x, x+strlen(x), m, args) cstr i = tofree(buffer_to_cstr(b)) debug_macros("unshift line: [%s]", i) vec_push(v, i) expand_macros_in_lines_inplace_with_new_private_prefix(v, ms) back_vec(xp, v, cstr): deq_unshift(lines, *xp) debug_macros(" done") use(l) ; use(before) # RM char *get_macro_args(char *p, vec *args, char *name, macrospace *ms): char *start_args = p char *tok char *after int paren_depth = 0 repeat: tok = p token_t tt = token(&p) which tt: TOK_EOT error("unbalanced parentheses in possible macro %s(%s", name, start_args) BRACKET if among(*tok, '(', '[', '{'): ++paren_depth else: --paren_depth if paren_depth < 0: *tok = '\0' done1 else . done1 debug_macros("all args for %s: [%s]", name, start_args) after = tok + 1 # expand macros in args new(v, vec, cstr, 1) vec_push(v, tofree(strdup(start_args))) expand_macros_in_lines_inplace(v, ms) start_args = *(cstr*)vec0(v) *tok = ')' paren_depth = 0 p = start_args char *val = start_args int tok_count = 0 while *p: char *tok = p token_t tt = token(&p) which tt: BRACKET if among(*tok, '(', '[', '{'): ++paren_depth else: --paren_depth DELIMIT if *tok == ',' && paren_depth == 0: *tok = '\0' macro_arg *arg = vec_push(args) arg->val = val arg->tok_count = tok_count val = p tok_count = 0 SPACE . else ++tok_count ++p # TODO set tok_count = 1 if all wrapped in ( ) or { } or [ ], check paren_depth if *val || veclen(args): macro_arg *arg = vec_push(args) arg->val = val arg->tok_count = tok_count debug_macros("args:") for_vec(i, args, macro_arg): use(i) debug_macros(" [%s] %d", i->val, i->tok_count) debug_macros("get_macro_args: after now pointing to: %s", after) return after int substitute_args_in_range(buffer *b, char *p0, char *p1, macro_def *m, vec *args): int tot_tok_count = 0 boolean wrapped = among(*p0, '(', '[', '{') int paren_depth = 0 char *p = p0 int i while p != p1: char *tok = p debug_macros("tokens remaining: %s", tok) token_t tt = token(&p) int tlen = p - tok if tt == NAME && args && veclen(args): i = 0 for_vec(param, m->params, cstr): int plen = strlen(*param) if tlen == plen && strncmp(*param, tok, plen) == 0: macro_arg *argp = (macro_arg *)v(args, i) cstr arg = argp->val int tok_count = argp->tok_count boolean wrap = !(buffer_last_char(b) == '(' && *p == ')') wrap = wrap && tok_count > 1 && !m->raw char last_char = cstr_last_char(arg) if among(last_char, '*', '&'): wrap = 0 boolean strip_spaces = m->raw != 2 if wrap: buffer_cat_char(b, '(') tot_tok_count += 2 if strip_spaces: while *arg == ' ': ++arg buffer_cat_cstr(b, arg) tot_tok_count += tok_count if wrap: buffer_cat_char(b, ')') subd ++i if tt == BRACKET: if among(*tok, '(', '[', '{'): ++paren_depth else: --paren_depth if p != p1: wrapped = 0 buffer_cat_range(b, tok, p) if tt != SPACE: ++tot_tok_count subd . if paren_depth: wrapped = 0 if wrapped: tot_tok_count = 1 return tot_tok_count use b_plain cz_header() grep(i, defines, vec*, **(cstr*)vec0(*i) != 'l') grep(i, includes, cstr, cstr_begins_with_word(*i, "export")) for_vec(i, extern_langs, vec*) grep(j, *i, cstr, **j != '\t' || strstr(*j, "export ")) if veclen(*i) <= 1 *i = NULL remove_null(extern_langs) # keep using_namespaces # keep enums vecclr(struct_union_class_template_protos) vecclr(struct_union_typedefs) # keep typedef # keep structs_unions_classes_templates grep(i, function_protos, cstr, !cstr_begins_with_word(*i, "local", "static")) grep(i, var_protos, cstr, !cstr_begins_with_word(*i, "local", "static")) for_vec(i, local_and_global_vars, vec*) cstr l = *(cstr*)vec0(*i) if !cstr_begins_with_word(l, "local", "static") vec_push(var_protos, fformat("extern %s", l)) vecclr(local_and_global_vars) vecclr(var_assignments) vecclr(functions) use b_plain hashtable *cz_includes_already vec *c_include cz_includes() NEW(cz_includes_already, hashtable, cstr_hash, cstr_eq, 53) NEW(c_include, vec, key_value, 32) vec *uses = resolve_deps_init_uses() vec_append(uses, includes) vec_free(includes) includes = uses do_includes() for_vec(i, c_include, key_value) cstr cmd = i->v ? "export" : "use" vec_push(includes, fformat("%s %s", cmd, i->k)) hashtable_free(cz_includes_already) vec_free(c_include) do_includes() cz_parse_t save cz_parse_save(&save) new(includes, vec, cstr, 64) for_vec(include1, save.includes, cstr) vecclr(includes) resolve_include(includes, *include1) for_vec(include, includes, cstr) cstr file = strchr(*include, ' ') *file++ = '\0' sh_unquote(file) if !already(cz_includes_already, file) cstr suffix = strrchr(file, '.') if suffix && (cstr_eq(suffix, ".bh") || cstr_eq(suffix, ".bbh")) if cz_standalone # lose the 'h' cstr_chop(suffix) include_file(file) else key_value *kv = vec_push(c_include) kv->k = file kv->v = (void*)(intptr_t)(**include == 'e') # use or export vecclr(save.includes) cz_parse_append_saved(&save) include_file(cstr file) # this might be inefficient! cz_parse_t save cz_parse_save(&save) if !is_file(file) error("could not find include file: %s", file) cstr suffix = strrchr(file, '.') cxx_using_c_lib = cz_lang == LANG_CXX && suffix && cstr_eq(suffix, ".bh") read_lines(lines, file) boolean is_header = !suffix || suffix[strlen(suffix)-1] == 'h' if is_header cz_parse() else b2b0() cxx_using_c_lib = 0 do_includes() cz_parse_prepend_saved(&save) #list_includes_structs() # for_vec(line, includes, cstr) # warn("%s", *line) # warn("") # for_vec(lines, structs_unions_classes_templates, vec*) # cstr l0 = *(cstr*)vec0(*lines) # warn("%s", l0) # warn("") use b_plain cstr version = "1.0" cstr description = "CZ translator - Sam Watkins, 2009" cstr usage[] = "[opts] prog [arg ...]", "[opts] -h [prog1 ...]", "--help", NULL cstr options[][3] = { "V", "version", "" }, { "v", "verbose", "" }, { "d", "dump", "dump configuration" }, { "c", "compile", "translate and compile only" }, { "t", "translate", "translate only" }, { "h", "headers", "generate headers" }, { "r", "run", "run only, do not compile" }, { "C", "clean", "recompile, remove unneeded files" }, { "1", "b1file", "generate b1 file" }, { "B", "nobackup", "don't backup files" }, { "u", "useb", "use b" }, { "m", "addmain", "add Main" }, { NULL, NULL, NULL } # TODO warn unknown options, check option arity # change get_options() to also read from config file/s and env vars Main() bm_enabled = 0 bm_start() O = get_options(options) boolean ok = 1 if opt("help") help() eif opt("version") version() else ok = cz_main_processor() exit(!ok) boolean cz_main_processor() boolean ok = 1 tofree_block() init() bm("done init") config() bm("done config") setup() bm("done setup") if opt("headers") boolean first = 1 eacharg(x) if first first = 0 else cz_parse_clear() src_file = x prepare() ok = cz_create_header() && ok else Getargs(cstr, src_file) prepare() bm("done prepare") if opt("dump") dump_settings() if opt("clean") clean() bm("done clean") ok = cz_compile_run() cz_free() return ok boolean cz_compile_run() boolean ok = 1 check_status() bm("done check") if need_compile && !opt("run") lock(lock_file) check_status() bm("done lock + check 2") if need_compile && !already_compiled if !opt("nobackup") do_backup() bm("done backup") ok = cz_translate_compile() bm("done translate / compile") if !file_size(log_file) remove(log_file) if ok && opt("clean") remove(c_file) if already_compiled error("%s has not been updated, check %s or try: fix %s", src_file, log_file, src_file) if ok && !opt("compile") && !opt("translate") cz_run() return ok check_status() NEW(src_stat, Stats, src_file) tofree(src_stat) if !stat_exists(src_stat) error("source file %s does not exist", src_file) NEW(exe_stat, Stats, exe_file) tofree(exe_stat) NEW(log_stat, Stats, log_file) tofree(log_stat) need_compile = !stat_exists(exe_stat) || exe_stat->st_size == 0 || exe_stat->st_mtime < src_stat->st_mtime || exe_stat->st_mtime < libb_stat->st_mtime already_compiled = stat_exists(log_stat) && src_stat->st_mtime < log_stat->st_mtime && log_stat->st_mtime > libb_stat->st_mtime do_backup() if exists(exe_file) remove(xbak_file) cp(exe_file, xbak_file) if file_cmp(src_file, bbak_file) remove(bbak2_file) rename(bbak_file, bbak2_file) cp(src_file, bbak_file) cz_run() if run_in_gdb cstr gdb = Which("gdb") cstr *gdbargs = Nalloc(cstr, args+4) gdbargs[0] = gdb gdbargs[1] = "--args" gdbargs[2] = exe_file memmove(gdbargs+3, arg, (args+1)*sizeof(cstr)) exe_file = gdb arg = gdbargs else --arg ; ++args ; arg[0] = exe_file if mingw && !*Getenv("MSYSTEM") && !run_in_gdb Atexit(hold_term_open) if cz_verbose decl(v, vec) array_to_vec(v, arg+1) warn_vec_cstr(exe_file, v) if mingw exit(Systemv(exe_file, arg)) # Execv doesn't wait on mingw! else Execv(exe_file, arg) use b_plain # TODO parse declarations properly in parse.b not like this cz_meta() new(mtype_buf, buffer, 128) boolean some_meta = 0 for_vec(i, structs_unions_classes_templates, vec*) cstr l = *(cstr*)vec0(*i) int n = veclen(*i) - 1 cstr type cstr name if (name = cstr_begins_with_word(l, "struct")) type = "STRUCT" eif (name = cstr_begins_with_word(l, "union")) type = "UNION" else continue char *p = strchr(name, ' ') if p name = tofree(Strndup(name, p-name)) New(v, vec, cstr, 16) vec_push(v, fformat("type__struct_union struct__t_%s =", name)) vec_push(v, fformat("\t{ TYPE_%s, \"%s\", sizeof(%s) }, %d,", type, name, name, n)) for(j, 0, n) l = *(cstr*)v(*i, j+1) + 1 cstr mname = strrchr(l, ' ') if !mname error("cz_meta: struct %s member lacks name", name) ++mname while *mname == '*' ++mname bufclr(mtype_buf) buffer_cat_range(mtype_buf, l, mname - (mname[-1] == ' ')) for_buffer(p, mtype_buf) which *p ' ' *p = '_' '*' *p = 'p' p = mname + strlen(mname)-1 while *p == ']' char *q = p while p >= mname+2 && p[-1] != '[' --p if p < mname+2 error("cz_meta: unmatched [] in array type: %s", l) buffer_cat_cstr(mtype_buf, "_a") buffer_cat_range(mtype_buf, p, q) p -= 2 p++ vec_push(v, fformat("\t\t{ (type*)t_%s, \"%.*s\", offsetof(%s, %.*s) }%s", buffer_to_cstr(mtype_buf), p-mname, mname, name, p-mname, mname, j==n-1 ? "" : ",")) vec_push(var_assignments, v) vec_push(defines, vec1(fformat("def t_%s &struct__t_%s", name, name))) # vec_push(v1, fformat("type__struct_union *t_%s = &struct__t_%s", name, name)) # vec_push(var_assignments, v1) some_meta = 1 if some_meta vec_push(includes, (cstr)"use ") buffer_free(mtype_buf) use b_plain typedef char *keymap_t(void *) def namespace_map_key(ns, key) ns->keymap ? ns->keymap(key) : key struct namespace namespace *parent keymap_t *keymap hashtable hash int namespace_buckets = 101 def namespace_init(ns) namespace_init(ns, NULL) def namespace_init(ns, parent) namespace_init(ns, parent, NULL) namespace_init(namespace *ns, namespace *parent, keymap_t *keymap) ns->parent = parent ns->keymap = keymap ? keymap : parent ? parent->keymap : NULL init(&ns->hash, hashtable, namespace_buckets) namespace_free(namespace *ns) hashtable_free(&ns->hash) namespace *namespace_child(namespace *ns) New(child, namespace, ns) return child int lookup_max_depth = 64 def namespace_lookup(ns, key) namespace_lookup(ns, key, 0) void *namespace_lookup(namespace *ns, void *key, int depth) cstr k = namespace_map_key(ns, key) if ++depth > lookup_max_depth error("namespace_lookup is caught in a loop while looking up %s", k) # XXX this is repeated in parent namespaces, # would they ever have different keymaps? let(kv, kv(&ns->hash, k)) if kv return kv->v if ns->parent return namespace_lookup(ns->parent, key, depth) return NULL void *namespace_lookup_skip(namespace *ns, void *key, int n) # this skips the first definition of something # and goes to the "next" one up, times n. if n == 0 return namespace_lookup(ns, key) if !ns->parent return NULL cstr k = namespace_map_key(ns, key) let(kv, kv(&ns->hash, k)) if kv --n return namespace_lookup_skip(ns->parent, key, n) # use let, not set! # set changes the meaning of the existing macro for parent scopes up to the last time it was defined but not above; # probably NOT what you want. # ((WTF @ this) # I could write a "kill" function and a "SET" function to completely redefine a macro for everyone, but YAGNI ATM namespace_set(namespace *ns, void *key, void *val) cstr k = namespace_map_key(ns, key) set(&ns->hash, Strdup(k), val) # NOTE The perl has this which was not used: # $self->{hash_order}{$k} = $self->{counter} namespace *namespace_let(namespace *ns, void *key, void *val) cstr k = namespace_map_key(ns, key) let(kv, kv(&ns->hash, k, val)) if kv->v != val ns = namespace_child(ns) set(&ns->hash, Strdup(k), val) return ns namespace *namespace_delete(namespace *ns, void *key) return namespace_let(ns, key, NULL) boolean namespace_defined(namespace *ns, void *key) return namespace_lookup(ns, key) != NULL use b_plain number_lines(cstr src_file) # FIXME do not put line numbers in macros or anything that ends up # in a .bh file... # but probably should do something when including such a file # hmm actually macro expansions should all report a single caller line number # for all lines of the expansion.. and possibly the line in the macro source # too, etc for nested macro expansions. complex cstr filename_quoted = qq(src_file) int line_no = 1 for_vec(i, lines, cstr) cstr l = *i # TODO use a function to get this data about the line int tabs = 0 int spaces = 0 boolean blank = 1 boolean is_comment = 0 boolean has_comment = 0 # has_comment is bogus because not tokenized, no matter boolean has_labels = 0 char *after_tabs = l for_cstr(j, l) if !isspace(*j) if *j == '#' has_comment = 1 if blank is_comment = 1 blank = 0 eif *j == '\t' if !has_comment ++tabs after_tabs = j+1 if !blank has_labels = 1 eif !spaces && blank while *j == ' ' ++spaces ; ++j --j if tabs > 0 && spaces == 0 && !blank && !is_comment if has_labels int len = after_tabs-l cstr o = tofree(Malloc(len+2)) strncpy(o, l, len) o[len] = '.' o[len+1] = '\0' vec_push(work, o) new(b, buffer, line_initial_space) buffer_cat_chars(b, '\t', tabs) Sprintf(b, "^line %d %s", line_no, filename_quoted) vec_push(work, tofree(buffer_to_cstr(b))) buffer_cat_chars(b, '\t', tabs) new(b1, buffer, line_initial_space) buffer_cat_chars(b1, '\t', tabs) buffer_cat_cstr(b1, after_tabs) vec_push(work, tofree(buffer_to_cstr(b1))) else new(b, buffer, line_initial_space) buffer_cat_chars(b, '\t', tabs) Sprintf(b, "^line %d %s", line_no, filename_quoted) vec_push(work, tofree(buffer_to_cstr(b))) vec_push(work, l) else vec_push(work, l) ++line_no swap(lines, work) vecclr(work) export vec types use util int lines_initial_space = 4096 int block_lines_initial_space = 256 int struct_union_names_space = 257 boolean cxx_using_c_lib = 0 boolean cz_section_comments = 0 boolean cz_section_comments_empty = 0 vec *lines, *work vec *defines, *cpp_directives, *includes, *extern_langs, *using_namespaces, *enums, *struct_union_class_template_protos, *struct_union_typedefs, *typedefs, *structs_unions_classes_templates, *function_protos, *var_protos, *local_and_global_vars, *var_assignments, *functions, *local_vars, *global_vars # vec *lines int cz_parse_vec_initial_space = 64 Def cz_parse_vov_names defines, extern_langs, enums, typedefs, structs_unions_classes_templates, var_assignments, functions, local_and_global_vars, local_vars, global_vars Def cz_parse_vos_names cpp_directives, includes, using_namespaces, struct_union_class_template_protos, struct_union_typedefs, function_protos, var_protos Def cz_parse_vec_names lines, work, cz_parse_vov_names, cz_parse_vos_names struct cz_parse_t vec *lines, *work vec *defines, *cpp_directives, *includes, *extern_langs, *using_namespaces, *enums, *struct_union_class_template_protos, *struct_union_typedefs, *typedefs, *structs_unions_classes_templates, *function_protos, *var_protos, *local_and_global_vars, *var_assignments, *functions, *local_vars, *global_vars boolean cz_parse_initd = 0 cz_parse_init() cz_parse_initd = 1 NEW(lines, vec, cstr, lines_initial_space) NEW(work, vec, cstr, lines_initial_space) call_each(cz_new_parse_vov, cz_parse_vov_names) call_each(cz_new_parse_vos, cz_parse_vos_names) def cz_new_parse_vov(x) NEW(x, vec, vec*, cz_parse_vec_initial_space) def cz_new_parse_vos(x) NEW(x, vec, cstr, cz_parse_vec_initial_space) cz_parse_free() vec_free(lines) vec_free(work) call_each(vov_free, cz_parse_vov_names) call_each(vec_free, cz_parse_vos_names) cz_parse_initd = 0 cz_parse_squeeze() call_each(vec_squeeze, cz_parse_vec_names) cz_parse_clear() call_each(vecclr, cz_parse_vec_names) cz_parse() boolean ok = 1 if !cz_parse_initd cz_parse_init() vec *block = NULL for_vec(i, lines, cstr) cstr l = *i char x = l[strspn(l, " \t")] if among(x, '\0', '#') continue if strchr(l, '\t') if !block error("unexpected block: %s", l) vec_push(block, l) continue if block vec_squeeze(block) block = NULL vec *v boolean is_block = 1 if cstr_begins_with_word(l, "use", "export") v = includes is_block = 0 eif cstr_begins_with_word(l, "using namespace") v = using_namespaces is_block = 0 eif cstr_begins_with_word(l, "def", "Def", "DEF", "ldef", "lDef", "lDEF") v = defines eif l[0] == '^' v = cpp_directives is_block = 0 eif cstr_begins_with_word(l, "struct", "union", "class", "template") && !possible_struct_is_func_returning_struct(l) && !strchr(l, '=') v = structs_unions_classes_templates eif cstr_begins_with_word(l, "enum") v = enums eif cstr_begins_with_word(l, "typedef") v = typedefs eif cstr_begins_with_word(l, "local", "static") && (strchr(l, '=') || !is_function(l)) v = local_vars eif cstr_begins_with(l, "extern \"") v = extern_langs eif cstr_begins_with_word(l, "extern") if cxx_using_c_lib l = cxx_using_c_lib_correct_proto(l) if strchr(l, ')') v = function_protos else v = var_protos is_block = 0 eif strchr(l, ')') && !strchr(l, '=') v = functions else v = global_vars if is_block NEW(block, vec, cstr, block_lines_initial_space) vec_push(v, block) if among(v, local_vars, global_vars) vec_push(local_and_global_vars, block) vec_push(block, l) else vec_push(v, l) for_vec(i, functions, vec*) if veclen(*i) == 1 cstr l = *(cstr*)vec0(*i) if cxx_using_c_lib l = cxx_using_c_lib_correct_proto(l) vec_push(function_protos, l) vec_free(*i) ; *i = NULL new(struct_union_names, hashtable, cstr_hash, cstr_eq, struct_union_names_space) for_vec(i, structs_unions_classes_templates, vec*) cstr l = *(cstr*)vec0(*i) if veclen(*i) == 1 vec_push(struct_union_class_template_protos, l) vec_free(*i) ; *i = NULL else cstr name = cstr_begins_with_word(l, "struct", "union") if name char *e = name if token_type(*e) == NAME ++e tok_name(e) name = tofree(Strndup(name, e-name)) if kv(struct_union_names, name, name)->v != name warn("multiple definitions of struct/union %s", name) ok = 0 for_vec(i, typedefs, vec*) if veclen(*i) == 1 cstr l = *(cstr*)vec0(*i) cstr p = cstr_begins_with_word(l, "typedef") assert(p, "invalid entry in typedefs vec") p = cstr_begins_with_word(p, "struct", "union") if p char *w2 = p if token_type(*w2) == NAME ++w2 tok_name(w2) if *w2 == ' ' int len = w2 - p ++w2 if strncmp(p, w2, len) == 0 && w2[len] == '\0' && get(struct_union_names, w2) vec_push(struct_union_typedefs, l) vec_free(*i) ; *i = NULL hashtable_free(struct_union_names) for_vec(i, local_and_global_vars, vec*) cstr l = *(cstr*)vec0(*i) if strchr(l, '=') vec_push(var_assignments, *i) *i = NULL call_each(remove_null, functions, structs_unions_classes_templates, typedefs, local_and_global_vars) if !ok Throw() boolean possible_struct_is_func_returning_struct(cstr l) char *p = strchr(l, ' ') if p && token_type(*++p) == NAME tok_name(p) while among(*p, ' ', '*', '&') ++p if token_type(*p) == NAME ++p tok_name(p) if *p == '(' return 1 return 0 boolean is_function(cstr l) char *p = strchr(l, '(') return p > l && char_name2(p[-1]) cstr cxx_using_c_lib_correct_proto(cstr l) if cstr_begins_with_word(l, "extern") l += 7 if token_type(*l) == STRING ++l tok_string(l) tok_space(l) l = fformat("extern \"C\" %s", l) return l cz_shuffle() adjacent("cpp directives", cpp_directives, 0) gappy("defines", defines, 1) adjacent("includes", includes, 0) adjacent("using namespaces", using_namespaces, 0) gappy("enums", enums, 1) adjacent("struct/union/class/template protos", struct_union_class_template_protos, 0) adjacent("struct/union typedefs", struct_union_typedefs, 0) adjacent("typedefs", typedefs, 1) gappy("structs/unions/classes/templates", structs_unions_classes_templates, 1) adjacent("extern langs", extern_langs, 1) adjacent("function protos", function_protos, 0) adjacent("var protos", var_protos, 0) adjacent("local and global vars", local_and_global_vars, 1) adjacent("var assignments", var_assignments, 1) gappy("functions", functions, 1) swap(lines, work) vecclr(work) def gappy(section, v, is_vov) output(section, v, is_vov, 1) def adjacent(section, v, is_vov) output(section, v, is_vov, 0) output(cstr section, vec *v, boolean is_vov, boolean gappy) boolean empty = !veclen(v) if empty && !cz_section_comments_empty return if cz_section_comments vec_push(work, fformat("# %s", section)) if !empty work_nl() if is_vov for_vec(i, v, vec*) for_vec(j, *i, cstr) vec_push(work, *j) if gappy work_nl() else for_vec(i, v, cstr) vec_push(work, *i) if gappy work_nl() repeat(cz_section_comments + !gappy) work_nl() def work_nl() vec_push(work, (cstr)"") int line_indent(cstr l) int tabs = 0 for_cstr(i, l) if *i == '\t' ++tabs return tabs cz_uniq() call_each(uniq_vovos, cz_parse_vov_names) call_each(uniq_vos, cz_parse_vos_names) cz_parse_save(cz_parse_t *s) call_each(_cz_parse_save_item, cz_parse_vec_names) cz_parse_init() def _cz_parse_save_item(x) s->x = x cz_parse_append_saved(cz_parse_t *s) call_each(_cz_parse_append_saved_item, cz_parse_vec_names) cz_parse_prepend_saved(cz_parse_t *s) call_each(_cz_parse_prepend_saved_item, cz_parse_vec_names) def _cz_parse_append_saved_item(x) vec_append(x, s->x) vec_free(s->x) Free(s->x) # XXX this is inefficient (n^2) as the accumulating part is copied each time # might it be better to use deqs for this reason? def _cz_parse_prepend_saved_item(x) vec_append(s->x, x) vec_free(x) Free(x) x = s->x cz_parse_restore(cz_parse_t *s) cz_parse_free() call_each(_cz_parse_restore_item, cz_parse_vec_names) cz_parse_initd = 1 def _cz_parse_restore_item(x) x = s->x use b_plain cz_pc() for_vec(function, functions, vec*) cstr *line_out = vec0(*function) int pc = 1 for_vec(line_p, *function, cstr) cstr line = *line_p if strstr(line, "b__pc") new(b, buffer, 256) char *p = line int could_be_blank = 0 while *p char *p0 = p token_t t = token(&p) int replaced = 0 if t == NAME && *p0 == 'b' && cstr_begins_with(p0, "b__pc") if tok_eq(p0, p, "b__pc") buffer_cat_int(b, pc) replaced = 1 eif tok_eq(p0, p, "b__pc_next") buffer_cat_int(b, pc+1) replaced = 1 eif tok_eq(p0, p, "b__pc_inc") ++pc replaced = 1 could_be_blank = 1 if !replaced buffer_cat_range(b, p0, p) line = *line_p = buffer_to_cstr(b) if could_be_blank && !*cstr_not_chr(line, '\t') continue # skip blank line from b__pc_inc *line_out++ = line vec_set_size(*function, line_out - (cstr *)vec0(*function)) cz_processes(): for_vec(lines, functions, vec*): cstr proto = *(cstr*)v(*lines, 0) boolean is_proc = cstr_begins_with(proto, "proc ") != NULL if is_proc: cz_processes__proc(proto, *lines) else: cz_processes__func(proto, *lines) cz_processes__func(cstr proto, vec *lines): use(proto) # In normal functions, remove any "state" prefix from variable decl. # This is so macros can use "state" decl and work in both procs and funcs. cstr *end = (cstr*)vecend(lines) cstr *lp = (cstr*)v(lines, 1) while lp != end cstr l = *lp char *p = strstr(l, "\tstate ") if p: p++ else: p = strstr(l, "\tfor state ") if p: p += 5 if p: *lp = tofree(Format("%.*s%s", p-l, l, p+6)) # 6 == strlen("state ") ++lp cz_processes__proc(cstr proto, vec *lines): use(proto) use(lines) # TODO use b_plain cz_protos() for_vec(i, structs_unions_classes_templates, vec*) cstr l = *(cstr*)vec0(*i) char *colon = strchr(l, ':') if colon while colon > l && colon[-1] == ' ' --colon l = tofree(Strndup(l, colon-l)) vec_push(struct_union_class_template_protos, l) for_vec(i, functions, vec*) cstr l = *(cstr*)vec0(*i) char *coloncolon = strstr(l, "::") if coloncolon && coloncolon < strchr(l, '(') && coloncolon[2] != ' ' continue vec_push(function_protos, l) for_vec(i, var_assignments, vec*) cstr l = *(cstr*)vec0(*i) char *eq = strchr(l, '=') if eq while eq > l && eq[-1] == ' ' --eq if !cstr_begins_with_word(l, "static", "local") l = fformat("extern %.*s", eq-l, l) else char *array = strstr(l, "[]") if array && array < eq continue l = fformat("%.*s", eq-l, l) vec_push(var_protos, l) use b_plain cz_resolve_deps() vec *to_include = resolve_deps_init_uses() for_vec(i, includes, cstr) resolve_include(to_include, *i) vec_free(includes) includes = to_include vec *resolve_deps_init_uses() vec *headerfiles = resolve_deps_init() vec *uses = headerfiles for_vec(i, uses, cstr) *i = fformat("use %s", *i) return uses vec *resolve_deps_init() vec_set_size(b_include_path, b_include_path_base_len) # TODO can I move this into setup() ? vec *files cz_new_parse_vos(files) for_vec(use, cz_use, cstr) resolve_dep(files, *use) vec_squeeze(files) return files resolve_dep(vec *out, cstr header) if header[0] == '/' && is_dir(header) vec_push(b_include_path, header) return if among(header[0], '"', '<', '/') vec_push_cstr(out, header) return # TODO optimize, use static vecs here...? new(c_names, vec, cstr, 4) new(b_names, vec, cstr, 4) new(dir_names, vec, cstr, 4) cstr name = tofree(Strdup(header)) cstr suffix = strrchr(name, '.') if suffix *suffix++ = '\0' if cz_lang != LANG_CXX && among(suffix[0], 'b', 'c') && suffix[1] == suffix[0] error("can't include bb/C++ code `%s' from b/C code", header) if suffix[0] == 'b' cstr b_dot_name = fformat(".%s", header) vec_push(b_names, header) vec_push(b_names, b_dot_name) else vec_push(c_names, header) if cz_lang == LANG_CXX && suffix[0] == 'h' && suffix[1] == 'h' vec_push(c_names, name) # a bit bogus ;) so "use rope.hh" -> #include else vec_push(dir_names, name) if cz_lang == LANG_CXX vec_push(c_names, name) cstr b_dot_name = fformat(".%s.bbh", name) vec_push(b_names, b_dot_name+1) vec_push(b_names, b_dot_name) # remove? just put C++ / C only libs as foo.b # in a separate dir under BRACE_LIB?? cstr b_dot_name = fformat(".%s.bh", name) vec_push(b_names, b_dot_name+1) vec_push(b_names, b_dot_name) # if veclen(b_names) && veclen(c_names) # error("internal error - confused between brace and C headers!") # # this can't be right? new(b, buffer, 256) if veclen(b_names) # it's a brace header, include dirs and ALL from B_INCLUDE_PATH in order new(matches, vec, cstr, 16) for_vec(dir, b_include_path, cstr) for_vec(name, b_names, cstr) buffer_clear(b) Sprintf(b, "%s" path__sep_cstr "%s", *dir, *name) cstr path = buffer_add_nul(b) if is_file(path) vec_push(matches, tofree(Strdup(path))) else char *end = path + strlen(path) char *dot = strrchr(path, '.') if end != path && dot && dot[1] == 'b' && end[-1] == 'h' end[-1] = '\0' if is_file(path) end[-1] = 'h' vec_push(matches, tofree(Strdup(path))) new(dir_matches, vec, cstr, 16) for_vec(dir, b_include_path, cstr) for_vec(name, dir_names, cstr) buffer_clear(b) Sprintf(b, "%s" path__sep_cstr "%s", *dir, *name) cstr path = buffer_add_nul(b) if is_dir(path) vec_push(dir_matches, tofree(Strdup(path))) if veclen(dir_matches) vec_append(b_include_path, dir_matches) # XXX b_include_path changes here, therefore need to reset it for each file XXX vec_free(dir_matches) if veclen(matches) vec_append(out, matches) if veclen(dir_matches) || veclen(matches) buffer_free(b) return if veclen(c_names) # its a C header - include the first found vec *c_include_path_ = LANG_CXX ? cxx_include_path : c_include_path for_vec(dir, c_include_path_, cstr) for_vec(name, c_names, cstr) buffer_clear(b) Sprintf(b, "%s" path__sep_cstr "%s", *dir, *name) cstr path = buffer_add_nul(b) if is_file(path) cstr inc if (*dir)[0] == '.' && (*dir)[1] == '\0' inc = Format("\"%s\"", *name) eif cz_resolve_includes inc = Format("\"%s\"", path) else inc = Format("<%s>", *name) vec_push(out, tofree(inc)) buffer_free(b) return buffer_free(b) # return
for cpp to presumably barf on it. # warn("could not resolve include file %s", header) vec_push(out, fformat("<%s>", header)) return resolve_include(vec *includes, cstr include) cstr command = include cstr file while *command == '\t' ++command file = cstr_begins_with(command, "use ") if !file file = cstr_begins_with(command, "export ") if !file || file[strcspn(file, " \t")] error("bad use statement: %s", include) int start = veclen(includes) resolve_dep(includes, file) new(b, buffer, 256) char tmp = file[-1] file[-1] = '\0' cstr *i = v(includes, start) cstr *e = vecend(includes) for ; i!=e; ++i cstr f = *i bufclr(b) if !among(*f, '<', '"') sh_quote(f, b) f = buffer_nul_terminate(b) *i = fformat("%s %s", command, f) # the perl version included the indent - not needed I think file[-1] = tmp buffer_free(b) use b_plain rm_comments_blank() for_vec(i, lines, cstr) cstr l = *i if strchr(l, '#') char *p = l while *p char *p0 = p if token(&p) == COMMENT # TODO more efficient, don't scan whole comment? while p0 > l && isspace(p0[-1]) --p0 *p0 = '\0' break int blank = is_blank(l) if !blank # remove colons and add func parens int len = strlen(l) if len > 2 && l[len-1] == ':' && l[len-2] != ' ': l[len-1] = '\0' if !strpbrk(l, "\t ()"): l = tofree(cstr_cat(l, "()")) # TODO alloc a little extra space for each line instead? # cope with vim adding unwanted spaces in indent char *from = strstr(l, " \t") if from char *to = from ++from while to > l && to[-1] == ' ' --to cstr_chop_start(to, from) vec_push(work, l) swap(lines, work) vecclr(work) use b_plain split_semicolons() for_vec(i, lines, cstr) cstr l = *i if strchr(l, ';') int indent = line_indent(l) int skip = 0 char *p = l while *p char *p0 = p token_t t = token(&p) if t == NAME && tok_eq(p0, p, "for") skip = 2 eif t == DELIMIT && *p0 == ';' && skip-- == 0 skip = 0 while p0 != l && p0[-1] == ' ' --p0 *p0 = '\0' if !is_blank(l) vec_push(work, tofree(Strdup(l))) while *p == ' ' ++p l = p repeat(indent) *--l = '\t' if !is_blank(l) vec_push(work, l) else vec_push(work, l) swap(lines, work) vecclr(work) use b_plain split_use() vec *out cz_new_parse_vos(out) for_vec(i, includes, cstr) cstr l = *i char *end = l+strlen(l) cstr command = l cstr word = Strchr(l, ' ')+1 char *e = strchr(word, ' ') if !e vec_push(out, l) else word[-1] = '\0' repeat if e *e = '\0' if !strlen(word) error("bad use statement: %s", nul_to_space(l, end)) vec_push(out, fformat("%s %s", command, word)) if !e break word = e + 1 while *word == ' ' ++word e = strchr(word, ' ') vec_squeeze(out) vec_free(includes) includes = out use b_plain split_vars() vec *vars vars = split_vars_1_vos(var_protos) vec_free(var_protos) var_protos = vars vars = split_vars_1_vov(local_and_global_vars) vec_free(local_and_global_vars) local_and_global_vars = vars vars = split_vars_1_vov(var_assignments) vec_clear(var_assignments) for_vec(i, vars, vec*) boolean assignment = 0 for_vec(j, *i, cstr) if strchr(*j, '=') assignment = 1 break if assignment vec_push(var_assignments, *i) else vec_push(local_and_global_vars, *i) vec_free(vars) vec *split_vars_1_vos(vec *in) vec *out cz_new_parse_vos(out) for_vec(i, in, cstr) cstr l = *i split_vars_1_s(out, l, l, 0) return out vec *split_vars_1_vov(vec *in) vec *out cz_new_parse_vov(out) for_vec(i, in, vec*) vec *decl = *i cstr l = *(cstr*)vec0(decl) if veclen(decl) > 1 # has a multi-line assignment; # assume it doesn't have multiple variables! vec_push(out, decl) continue split_vars_1_s(out, l, decl, 1) return out split_vars_1_s(vec *out, cstr l, void *decl, boolean is_vov) # this is FUGLY if !strchr(l, ',') vec_push(out, decl) return # we might have multiple variables # FIXME this splitter does not handle all possible C syntax # method: read tokens until find a , notice = [] # before that, and position of name which is previous # token char *type = l int typelen = 0 char *name = NULL int namelen = 0 char *array = NULL char *assign = NULL char *p = l char *after_delim = NULL int bracket_depth = 0 repeat char *p0 = p token_t t = token(&p) if t == BRACKET if bracket_depth == 0 && *p0 == '[' && !assign array = p0 bracket_depth += bracket_type(*p0) if bracket_depth != 0 if t == TOK_EOT error("mismatched brackets: %s", l) continue if t == NAME && !array && !assign name = p0 namelen = p - p0 eif *p0 == '=' && p == p0+1 assign = p0 eif t == TOK_EOT || *p0 == ',' while name > l && among(name[-1], '*', '&') --name ; ++namelen # char *c is ok, char* c is not. if name && !typelen typelen = name - type if !name || !namelen || !typelen || (after_delim && name != after_delim) # too complicated or wrong! # don't split it vec_push(out, decl) break while p0 > l && p0[-1] == ' ' --p0 new(b, buffer, 128) buffer_cat_range(b, type, type+typelen) buffer_cat_range(b, name, p0) cstr l2 = buffer_to_cstr(b) if is_vov New(v, vec, cstr, 1) vec_push(v, l2) vec_push(out, v) else vec_push(out, l2) if t == TOK_EOT break after_delim = p0 + 1 while *after_delim == ' ' ++after_delim if !*after_delim break name = NULL namelen = 0 array = NULL assign = NULL use b_plain # TODO def cz_strip() new(top_funcs, vec, cstr, 1) vec_push(top_funcs, (cstr)"main") cz_strip(top_funcs) cz_strip(vec *top_funcs) use(top_funcs) # RM # TODO use util error export types #typedef enum { COMMENT, TABS, SPACE, NUMBER, STRING, CHARACTER, NAME, BRACKET, DELIMIT, OP, ILLEGAL } token_types typedef enum { NAME, SPACE, BRACKET, OP, NEWLINE, TABS, DELIMIT, NUMBER, COMMENT, STRING, CHARACTER, BSPACE, TOKEN_TYPE_TOP, ILLEGAL, TOK_EOT } token_t # tried to order with commonest ones first... probably should use stats to do that :p lol # what is QUOTED good for? symbols? like in lisp? code as data? # disabled for now (cause hardish to implement!) boolean tok_initd = 0 typedef char char_table[256] char_table ct_token_type, ct_name2 # TODO more tables for different types of characters? tok_init() if !tok_initd tok_initd = 1 char_table_init(ct_token_type, c, token_type_(c)) char_table_init(ct_name2, c, char_name2_(c)) def char_name2_(c) tween(c, 'A', 'Z') || tween(c, 'a', 'z') || tween(c, '0', '9') || among(c, '_', '$') def token_type(c) ct_token_type[(int)(uchar)c] def char_name2(c) ct_name2[(int)(uchar)(c)] char token_type_(char c) if c == '\0' return TOK_EOT eif c == '#' return COMMENT eif c == '\n' return NEWLINE eif c == '\t' return TABS eif c == ' ' return SPACE eif c == '\b' return BSPACE eif tween(c, '0', '9') return NUMBER # I want to be able to identify a token's type from its first char. # So can't allow .1, you must type 0.1 :/ eif c == '"' return STRING eif c == '\'' return CHARACTER # eif c == '`' # return QUOTED eif tween(c, 'A', 'Z') || tween(c, 'a', 'z') || among(c, '_', '$') return NAME eif among(c, '[', ']', '(', ')', '{', '}') return BRACKET eif among(c, ';', ',') return DELIMIT eif tween(c, 33, 126) return OP else return ILLEGAL def char_table_init(array, var, predicate) . int var for var=0 ; var<256 ; ++var char *p = array + var *p = predicate def tok_EOT '\0' # this can be over-ridden to be \n instead def tok_comment(i) while *i != tok_EOT ++i def tok_tabs(i) while *i == '\t' ++i def tok_space(i) while *i == ' ' ++i def tok_bspace(i) while *i == '\b' ++i def tok_number(i) if *(i-1) != '0' tok_decimal(i) eif *i == 'x' ++i tok_hex(i) eif tween(*i, '0', '7') tok_octal(i) eif !tween(*i, '0', '9') tok_decimal(i) # otherwise - a BAD number, at this tokenizer stage it will be split 08 -> 0 8 for example :p def tok_decimal(i) tok_decimal_int(i) if *i == '.' ++i tok_decimal_int(i) tok_float_exp(i) tok_float_suffix(i) def tok_decimal_int(i) while tween(*i, '0', '9') ++i def tok_float_exp(i) if among(*i, 'e', 'E') ++i if among(*i, '+', '-') ++i tok_decimal_int(i) def tok_float_suffix(i) if among(*i, 'f', 'F', 'l', 'L') ++i def tok_hex(i) while tween(*i, '0', '9') || tween(*i, 'A', 'F') || tween(*i, 'a', 'f') ++i def tok_octal(i) while tween(*i, '0', '7') ++i def tok_string(i) while !among(*i, '"', tok_EOT) if *i == '\\' ++i if *i == tok_EOT break ++i if *i == '"' ++i def tok_char(i) while !among(*i, '\'', tok_EOT) if *i == '\\' ++i if *i == tok_EOT break ++i if *i == '\'' ++i def tok_name(i) while char_name2(*i) ++i def tok_bracket(i) . def tok_delimit(i) . def tok_op(i) while token_type(*i) == OP ++i def tok_illegal(i) while token_type(*i) == ILLEGAL && *i != tok_EOT ++i def bracket_type(c) among(c, '[', '(', '{') ? 1 : -1 token_t token(char **i_ptr) char *i = *i_ptr token_t t = token_type(*i++) which t TOK_EOT . COMMENT tok_comment(i) NEWLINE . TABS tok_tabs(i) SPACE tok_space(i) BSPACE tok_bspace(i) NUMBER tok_number(i) STRING tok_string(i) CHARACTER tok_char(i) NAME tok_name(i) BRACKET tok_bracket(i) DELIMIT tok_delimit(i) OP tok_op(i) ILLEGAL tok_illegal(i) else fault("unknown token type here: %s", *i_ptr) *i_ptr = i return t boolean tok_eq(char *p0, char *p1, cstr s) while p0 != p1 if *p0++ != *s++ return 0 return *s == '\0' use b_plain cz_typedefs() for_vec(i, struct_union_class_template_protos, cstr) cstr l = *i cstr name = cstr_begins_with_word(l, "struct", "union") if name && token_type(*name) == NAME char *e = name + 1 tok_name(e) if *e == '\0' int struct_or_union_len = name - l while l[struct_or_union_len-1] == ' ' --struct_or_union_len vec_push(struct_union_typedefs, fformat("typedef %.*s %s %s", struct_or_union_len, l, name, name)) # *i = NULL # remove_null(struct_union_class_template_protos) use b_plain opts *O int line_initial_space = 512 cstr cz_config_file cstr src_file, exe_file, xbak_file, c_file, b1_file, bh_file, bbak_file, bbak2_file, log_file, lock_file cstr cz_dir, cz_lib, cz_inc, cz_cc, cz_cxx, libb, gcc_target, gcc_version, gcc_major boolean cz_debug, cz_optimize, cz_profile, cz_standalone, cz_plain, cz_verbose, cz_line_numbers, cz_resolve_includes, cz_bh_no_macros boolean run_in_gdb boolean need_compile, already_compiled vec *cflags, *cxxflags, *ldflags, *ldlibs, *cz_use, *include_path, *b_include_path, *c_include_path, *cxx_include_path int b_include_path_base_len enum { LANG_C, LANG_CXX } cz_lang Stats *libb_stat, *src_stat, *exe_stat, *log_stat cstr cz_L_so, cz_use_s, c_ext, b_ext, srcdir, srcbase, srcname cstr src_file_data init() tok_init() cz_parse_init() NEW(cflags, vec, cstr, 16) NEW(cxxflags, vec, cstr, 16) NEW(ldflags, vec, cstr, 16) NEW(ldlibs, vec, cstr, 16) NEW(cz_use, vec, cstr, 16) NEW(include_path, vec, cstr, 16) NEW(c_include_path, vec, cstr, 16) NEW(b_include_path, vec, cstr, 16) NEW(cxx_include_path, vec, cstr, 16) cz_free() cz_parse_free() vec_free(cflags) vec_free(cxxflags) vec_free(ldflags) vec_free(ldlibs) vec_free(cz_use) vec_free(include_path) vec_free(c_include_path) vec_free(b_include_path) vec_free(cxx_include_path) # other stuff is freed by tofree_block # TODO tok_free() ? config() cz_config_file = tofree(dotfile(".cz")) if exists(cz_config_file) load_config(cz_config_file) cz_lib = Getenv("CZ_LIB") cz_inc = Getenv("CZ_INC") cstr debug_s = Getenv("DEBUG") cz_debug = boolean(debug_s) run_in_gdb = cstr_eq(debug_s, "gdb") cz_optimize = boolean(Getenv("OPTIMIZE", "0")) cz_profile = boolean(Getenv("PROFILE")) cz_standalone = boolean(Getenv("CZ_STANDALONE")) cz_plain = boolean(Getenv("CZ_PLAIN")) cz_verbose = boolean(Getenv("CZ_VERBOSE")) || opt("verbose") cz_line_numbers = boolean(Getenv("CZ_LINE_NUMBERS")) cz_resolve_includes = boolean(Getenv("CZ_RESOLVE_INCLUDES")) cz_bh_no_macros = boolean(Getenv("CZ_BH_NO_MACROS")) cz_lang = cstr_eq(Getenv("CZ_LANG"), "C++") ? LANG_CXX : LANG_C if cstr_eq(program, "cz++") || cstr_case_eq(dflt(opt("lang")), "c++") cz_lang = LANG_CXX splitv(cflags, Getenv("CFLAGS"), ' ') splitv(cxxflags, Getenv("CXXFLAGS"), ' ') splitv(ldflags, Getenv("LDFLAGS"), ' ') splitv(ldlibs, Getenv("LDLIBS"), ' ') splitv(cz_use, Getenv("CZ_USE"), ' ') splitv(include_path, Getenv("INCLUDE_PATH"), PATH_sep) gcc_target = Getenv_or_null("GCC_TARGET") gcc_version = Getenv_or_null("GCC_VERSION") cz_dir_to_lib() cz_lib = tofree(path_cat(cz_dir, "lib")) libb = tofree(path_cat(cz_lib, "libb" SO)) NEW(libb_stat, Stats, libb) tofree(libb_stat) setup() cstr cz_bin = program_dir cz_dir = dir_name(tofree(Strdup(cz_bin))) if !*cz_lib cz_dir_to_lib() if !stat_exists(libb_stat) cz_dir = NULL cstr brace = tofree(which("brace")) if brace cz_bin = dir_name(brace) cz_dir = dir_name(tofree(Strdup(cz_bin))) eif mingw each(d, (cstr)"c:\\cz", "c:\\Program Files\\cz", "d:\\cz", "d:\\Program Files\\cz") if is_dir(d) cz_dir = d cz_bin = tofree(path_cat(cz_dir, "bin")) break if cz_dir cz_lib = tofree(path_cat(cz_dir, "lib")) libb = tofree(path_cat(cz_lib, "libb" SO)) cz_dir_to_lib() if !opt("translate") && !stat_exists(libb_stat) error("please ensure cz, libb and brace are installed and in your PATH") if !*cz_inc cz_inc = tofree(path_cat(cz_dir, "include")) if !is_dir(cz_inc) Free(cz_inc) cz_inc = cz_lib PATH_prepend(cz_bin) bm("done setup 1") vec_push_cstr(ldlibs, "-lm") if cz_standalone cz_L_so = "" else cz_L_so = fformat("-L%s", cz_lib) new(l_libb, buffer, 16) buffer_cat_cstr(l_libb, "-lb") if cz_plain buffer_cat_cstr(l_libb, "_plain") if cz_debug buffer_cat_cstr(l_libb, "_debug") vec_push_cstr(ldlibs, tofree(buffer_to_cstr(l_libb))) cstr I_inc = fformat("-I%s", cz_inc) each(x, (cstr)"-Wall", "-Wextra", "-Wno-long-long", "-I.", I_inc, "-D_GNU_SOURCE", "-D_FILE_OFFSET_BITS=64") vec_push_cstr(cflags, x) if cz_debug vec_push_cstr(cflags, "-ggdb") if cz_optimize vec_push_cstr(cflags, "-O2") if cz_profile vec_push_cstr(cflags, "-pg") bm("done setup 2") vec_push_cstr(cz_use, "cz") each(x, (cstr)"-lm") vec_push_cstr(ldlibs, x) if !cz_plain: vec_push_cstr(cflags, "-I/usr/include/SDL2") # vec_push_cstr(cflags, "-I/usr/include/SDL") each(x, (cstr)"-lpng", "-lSDL2_image", "-lSDL2_mixer", "-lSDL2_ttf", "-lSDL2") # each(x, (cstr)"-lpng", "-lSDL", "-lSDL_mixer") vec_push_cstr(ldlibs, x) if mingw each(x, (cstr)"mingw", "gnu-", "bsd-", "linux-", "modern-", "io-select", "pc") vec_push_cstr(cz_use, x) vec_push_cstr(ldflags, "-Wl,--enable-auto-import,--enable-runtime-pseudo-reloc") vec_push_cstr(ldlibs, "-lws2_32") if !cz_plain each(x, (cstr)"-lopengl32", "-lglu32", "-lgdi32") vec_push_cstr(ldlibs, x) else each(x, (cstr)"unix", "mingw-") vec_push_cstr(cz_use, x) if !cz_plain if exists("/usr/include/X11/Xlib.h") || exists("/usr/X11R6/include/X11/Xlib.h") || exists("/mnt/utmp/codeblocks/usr/include/X11/Xlib.h") vec_push_cstr(cz_use, "x") vec_push_cstr(cflags, "-I/usr/X11R6/include") vec_push_cstr(ldflags, "-L/usr/X11R6/lib") each(x, (cstr)"-lX11", "-lXext") vec_push_cstr(ldlibs, x) else vec_push_cstr(cz_use, "gr_dummy") vec_push_cstr(ldlibs, "-lcrypt") decl(un, struct utsname) uname(un) cstr os = tofree(Strdup(un->sysname)) cstr release = un->release # boolean arm = cstr_begins_with(un->machine, "arm") != NULL lc(os) char *p = strchr(release, '-') if p *p = '\0' if strstr(os, "bsd") || cstr_eq(os, "sunos") || cstr_eq(os, "dragonfly") || cstr_eq(os, "darwin"): vec_push_cstr(cz_use, "bsd") if strstr(os, "netbsd") || strstr(os, "openbsd"): vec_push_cstr(cz_use, "modern-") else: vec_push_cstr(cz_use, "modern") else vec_push_cstr(cz_use, "bsd-") vec_push_cstr(cz_use, os) if !cstr_eq(os, "linux") && !cstr_eq(os, "hurd") && !cstr_eq(os, "gnu") vec_push_cstr(cz_use, "gnu-") if cstr_eq(os, "linux") if version_ge(release, "2.6.20") vec_push_cstr(cz_use, "modern") vec_push_cstr(cz_use, "io-select") # !! epoll doesn't work on regular files ?? # if arm: # vec_push_cstr(cz_use, "io-select") # else: # vec_push_cstr(cz_use, "io-epoll") else each(x, (cstr)"modern-", "io-select") vec_push_cstr(cz_use, x) else each(x, (cstr)"linux-", "io-select") vec_push_cstr(cz_use, x) # devices: TODO N900, etc cstr device if exists("/proc/pandora"): device = "pandora" vec_push_cstr(cz_use, "gles") if !cz_plain: each(x, (cstr)"-lEGL", "-lGLES_CM", "-lpvrPVR2D_X11WSEGL"): vec_push_cstr(ldlibs, x) else: device = "pc" if !cz_plain: vec_push_cstr(ldlibs, "-lGL") vec_push_cstr(cz_use, device) bm("done setup 3") grep(i, cz_use, cstr, cz_use_exists(*i)) uniq_vos(cz_use) vec_squeeze(cz_use) cz_use_s = tofree(joinv(' ', cz_use)) bm("done setup 4") if !(cz_cc = Getenv_or_null("CC")) each(cc, (cstr)"gcc", "tcc", "cc") if tofree(which(cc)) cz_cc = cc break if !cz_debug if !cstr_eq(cz_cc, "tcc") && !cstr_ends_with(cz_cc, "/tcc") vec_push_cstr(ldflags, "-s") # TODO run strip when using tcc? if !(cz_cxx = Getenv_or_null("CXX")) each(cc, (cstr)"g++", "c++") if tofree(which(cc)) cz_cxx = cc break if !cz_cxx cz_cxx = cz_cc bm("done setup 5") if cz_cxx[0] == 'g' vec_push_cstr(cxxflags, "-Weffc++") vec_append(cxxflags, cflags) if cz_cc[0] == 'g' each(x, (cstr)"-pedantic", "-std=gnu99") vec_push_cstr(cflags, x) # TODO recheck if gcc executable is newer than config file :/ if !gcc_target || !gcc_version if !gcc_target gcc_target = tofree(chomp(x("gcc -dumpmachine"))) if !gcc_version gcc_version = tofree(chomp(x("gcc -dumpversion"))) if !exists(cz_config_file) F_out(cz_config_file) sf("GCC_TARGET=\"%s\"", gcc_target) sf("GCC_VERSION=\"%s\"", gcc_version) gcc_major = Strdup(gcc_version) char *p = strchr(gcc_version, '.') if p: p = strchr(p+1, '.') if p: *p = '\0' bm("done setup 6") each(x, b_include_path, c_include_path, cxx_include_path) vec_push(x, (cstr)".") splitv(b_include_path, cz_inc) b_include_path_base_len = veclen(b_include_path) each(x, (cstr) "/MinGW/include/c++/3.2.3", fformat("/usr/include/c++/%s", gcc_major)) vec_push(cxx_include_path, x) each(x, (cstr) "/MinGW/include", "/usr/local/include", fformat("/usr/lib/gcc/%s/%s/include", gcc_target, gcc_version), fformat("/usr/lib/gcc-lib/%s/%s/include", gcc_target, gcc_version), fformat("/usr/%s/include", gcc_target), "/usr/include") vec_push(c_include_path, x) vec_push(cxx_include_path, x) bm("done setup 7") new(dircache, hashtable, cstr_hash, cstr_eq, 53) each(v, c_include_path, cxx_include_path) vec_append(v, include_path) uniqo(v, cstr_hash, cstr_eq) grep(i, v, cstr, cache(dircache, *i, is_dir(*i))) vec_squeeze(v) hashtable_free(dircache) bm("done setup 8") boolean cz_use_exists(cstr u) cstr dir = path_cat(cz_inc, u) boolean rv = is_dir(dir) Free(dir) return rv prepare() # this is for setup that is specific to a src_file fix_path(src_file) src_file = tofree(readlinks(Strdup(src_file))) if cstr_ends_with(src_file, ".b") cz_lang = LANG_C eif cstr_ends_with(src_file, ".bb") cz_lang = LANG_CXX else cstr hb = hashbang(src_file) if hb if strstr(hb, "/bb") || strstr(hb, "/cz++") || \ strstr(hb, " bb") || strstr(hb, " cz++") cz_lang = LANG_CXX Free(hb) if cz_lang == LANG_CXX c_ext = "cc" b_ext = "bb" else c_ext = "c" b_ext = "b" dirbase db = dirbasename(tofree(Strdup(src_file))) srcdir = db.dir # srcbase = db.base # srcname = tofree(Strdup(srcbase)) srcname = db.base cstr dot = strrchr(srcname, '.') if dot *dot = '\0' exe_file = fformat("%s" path__sep_cstr ".%s" EXE, srcdir, srcname) lock_file = fformat("%s" path__sep_cstr ".%s.lock", srcdir, srcname) xbak_file = fformat("%s" path__sep_cstr ".%s.xbak" EXE, srcdir, srcname) c_file = fformat("%s" path__sep_cstr ".%s.%s", srcdir, srcname, c_ext) b1_file = fformat("%s" path__sep_cstr ".%s.%s1", srcdir, srcname, b_ext) bh_file = fformat("%s" path__sep_cstr ".%s.%sh", srcdir, srcname, b_ext) bbak_file = fformat("%s" path__sep_cstr ".%s.bak", srcdir, srcname) bbak2_file = fformat("%s" path__sep_cstr ".%s.bak2", srcdir, srcname) log_file = fformat("%s" path__sep_cstr ".%s.log", srcdir, srcname) system_verbose = cz_verbose # FIXME change / remove BRACE->CZ and LIB/INC, SO/LIB Setenv("BRACE_USE", cz_use_s) Setenv("BRACE_LIB", cz_inc) Setenv("BRACE_SO", cz_lib) dump_settings() warn("options:") dump_options(O) # TODO I need a "stringify" syntax for macros, so could just do # dumpvar(src_file). cpp has one. warn("PATH: %s", Getenv("PATH")) warn("src_file: %s", src_file) warn("exe_file: %s", exe_file) warn("xbak_file: %s", xbak_file) warn("c_file: %s", c_file) warn("bbak_file: %s", bbak_file) warn("bbak2_file: %s", bbak2_file) warn("log_file: %s", log_file) warn("lock_file: %s", lock_file) warn("cz_dir: %s", cz_dir) warn("cz_lib: %s", cz_lib) warn("cz_inc: %s", cz_inc) warn("cz_debug: %d", cz_debug) warn("cz_optimize: %d", cz_optimize) warn("cz_standalone: %d", cz_standalone) warn("cz_plain: %d", cz_plain) warn("cz_verbose: %d", cz_verbose) warn("cz_line_numbers: %d", cz_line_numbers) warn("cz_lang: %d", cz_lang) warn("cz_cc: %s", cz_cc) warn_vec_cstr("cflags:", cflags) warn_vec_cstr("cxxflags:", cxxflags) warn_vec_cstr("ldflags:", ldflags) warn_vec_cstr("ldlibs:", ldlibs) warn_vec_cstr("cz_use:", cz_use) warn("cz_use_s: %s", cz_use_s) warn("gcc_target: %s", gcc_target) warn("gcc_version: %s", gcc_version) warn("gcc_major: %s", gcc_major) warn_vec_cstr("include_path:", include_path) warn_vec_cstr("b_include_path:", b_include_path) warn_vec_cstr("c_include_path:", c_include_path) warn_vec_cstr("cxx_include_path:", cxx_include_path) cz_wrappers() . use b_plain struct macrospace namespace ns hashtable vague struct macro_key cstr name int n_args # -1 means no parens macrospace_init(macrospace *ms, macrospace *parent) init(&ms->vague, hashtable, namespace_buckets) init(&ms->ns, namespace, &parent->ns, macro_keymap) struct macro_def cstr name vec *params vec *lines flag ldef:1 flag block:1 flag raw:2 struct macro_arg cstr val int tok_count macro_def_init(macro_def *m, cstr name, vec *params, vec *lines, boolean block, boolean ldef, int raw) m->name = name ; m->params = params ; m->lines = lines m->block = block ; m->ldef = ldef ; m->raw = raw def macrospace_lookup_vague(ms, name) macrospace_lookup_vague(ms, name, 0) boolean macrospace_lookup_vague(macrospace *ms, cstr name, int depth) if ++depth > lookup_max_depth error("macrospace_lookup_vague is caught in a loop while looking up %s", name) return get(&ms->vague, name, 0) || (ms->ns.parent && macrospace_lookup_vague((macrospace *)ms->ns.parent, name, depth)) # I thought I was meant to use let not set??? macrospace_set(macrospace *ms, macro_key *key, void *value) if value set(&ms->vague, key->name, 1) namespace_set(&ms->ns, key, value) # XXX this uses a static buffer char *macro_keymap(void *key) static buffer *b = NULL if !b NEW(b, buffer, 64) macro_key *mk = key if mk->n_args == -1 return mk->name bufclr(b) buffer_cat_cstr(b, mk->name) buffer_cat_char(b, '(') if mk->n_args >= 1 buffer_cat_char(b, '_') repeat(mk->n_args - 1) buffer_cat_cstr(b, ", _") buffer_cat_char(b, ')') return buffer_add_nul(b) macrospace_dump(macrospace *ms) new(ks, vec, cstr, 64) keys(ks, &ms->ns.hash) for_vec(k, ks, cstr) cstr name = *k warn("lookup macro by key: %s", name) macro_def *m = Get(&ms->ns.hash, name) int n_args = m->params ? veclen(m->params) : -1 warn("name: %s", name) warn("n_args: %d", n_args) if n_args >= 0 Fprint(stderr, "args: ") for_vec(a, m->params, cstr) Fprintf(stderr, "%s ", *a) nl(stderr) warn("lines:") for_vec(l, m->lines, cstr) Fsayf(stderr, " %s", *l) warn("opts: l %d b %d raw %d", m->ldef, m->block, m->raw) nl(stderr) if ms->ns.parent macrospace_dump((macrospace *)ms->ns.parent)