diff --git a/examples/metagen.odin b/examples/metagen.odin deleted file mode 100644 index d88d28b25..000000000 --- a/examples/metagen.odin +++ /dev/null @@ -1,595 +0,0 @@ -#import "fmt.odin"; -#import "os.odin"; -#import "utf8.odin"; -#import win32 "sys/windows.odin"; - -alloc_ucs2_to_utf8 :: proc(wstr: ^u16) -> string { - wstr_len := 0; - for (wstr+wstr_len)^ != 0 { - wstr_len++; - } - len := 2*wstr_len-1; - buf := new_slice(byte, len+1); - str := slice_ptr(wstr, wstr_len+1); - - - i, j := 0, 0; - for str[j] != 0 { - match { - case str[j] < 0x80: - if i+1 > len { - return ""; - } - buf[i] = cast(byte)str[j]; i++; - j++; - case str[j] < 0x800: - if i+2 > len { - return ""; - } - buf[i] = cast(byte)(0xc0 + (str[j]>>6)); i++; - buf[i] = cast(byte)(0x80 + (str[j]&0x3f)); i++; - j++; - case 0xd800 <= str[j] && str[j] < 0xdc00: - if i+4 > len { - return ""; - } - c := cast(rune)((str[j] - 0xd800) << 10) + cast(rune)((str[j+1]) - 0xdc00) + 0x10000; - buf[i] = cast(byte)(0xf0 + (c >> 18)); i++; - buf[i] = cast(byte)(0x80 + ((c >> 12) & 0x3f)); i++; - buf[i] = cast(byte)(0x80 + ((c >> 6) & 0x3f)); i++; - buf[i] = cast(byte)(0x80 + ((c ) & 0x3f)); i++; - j += 2; - case 0xdc00 <= str[j] && str[j] < 0xe000: - return ""; - default: - if i+3 > len { - return ""; - } - buf[i] = 0xe0 + cast(byte) (str[j] >> 12); i++; - buf[i] = 0x80 + cast(byte)((str[j] >> 6) & 0x3f); i++; - buf[i] = 0x80 + cast(byte)((str[j] ) & 0x3f); i++; - j++; - } - } - - return cast(string)buf[..i]; -} - -is_whitespace :: proc(b: byte) -> bool { - match b { - case ' ', '\t', '\n', '\r', '\v', '\f': - return true; - } - return false; -} - -is_letter :: proc(b: byte) -> bool { - match { - case 'A' <= b && b <= 'Z', - 'a' <= b && b <= 'z', - '_' == b: - return true; - } - return false; -} - -is_digit :: proc(b: byte) -> bool { - match { - case '0' <= b && b <= '9': - return true; - } - return false; -} - - -trim :: proc(s: string) -> string { - return trim_right(trim_left(s)); -} - -trim_left :: proc(s: string) -> string { - start := 0; - for i in 0..s.count { - if is_whitespace(s[i]) { - start++; - } else { - break; - } - } - - return s[start..]; -} - - -trim_right :: proc(s: string) -> string { - end := s.count; - for i := end-1; i >= 0; i-- { - if is_whitespace(s[i]) { - end--; - } else { - break; - } - } - - return s[0..end]; -} - -errorf :: proc(format: string, args: ..any) { - fmt.fprintf(os.stderr, format, ..args); - os.exit(1); -} - -errorln :: proc(args: ..any) { - fmt.fprintln(os.stderr, ..args); - os.exit(1); -} - -output_filename :: proc(s: string) -> string { - ext := "metagen"; - cext := "c"; - - i := s.count-ext.count; - str := new_slice(byte, i+cext.count); - copy(str, cast([]byte)s[..i]); - str[i] = 'c'; - return cast(string)str; -} - -Tokenizer :: struct { - filename: string, - data: []byte, - curr: int, - read_curr: int, - line: int, - line_count: int, - - curr_rune: rune, - error_count: int, -} - -Token_Kind :: enum { - INVALID, - IDENT, - STRING, - EQUAL, - COMMA, - SEMICOLON, - OTHER, - EOF, -} - -Token :: struct { - kind: Token_Kind, - text: string, - line: int, - column: int, -} - -tokenizer_err :: proc(t: ^Tokenizer, msg: string, args: ..any) { - column := max(t.read_curr - t.line+1, 1); - - fmt.fprintf(os.stderr, "%s(%d:%d) Syntax error: ", t.filename, t.line_count, column); - fmt.fprintf(os.stderr, msg, ..args); - fmt.fprintln(os.stderr); - t.error_count++; - - if t.error_count > 10 { - os.exit(1); - } -} - -advance_to_next_rune :: proc(t: ^Tokenizer) { - if t.read_curr < t.data.count { - t.curr = t.read_curr; - if t.curr_rune == '\n' { - t.line = t.curr; - t.line_count++; - } - r := cast(rune)t.data[t.read_curr]; - width := 1; - if r == 0 { - tokenizer_err(t, "Illegal character NULL"); - } else if r >= 0x80 { - r, width = utf8.decode_rune(t.data[t.read_curr..]); - if r == utf8.RUNE_ERROR && width == 1 { - tokenizer_err(t, "Illegal UTF-8 encoding"); - } else if r == utf8.RUNE_BOM && t.curr > 0 { - tokenizer_err(t, "Illegal byte order mark"); - } - } - t.read_curr += width; - t.curr_rune = r; - } else { - t.curr = t.data.count; - if t.curr_rune == '\n' { - t.line = t.curr; - t.line_count++; - } - t.curr_rune = utf8.RUNE_EOF; - } -} - -skip_whitespace :: proc(t: ^Tokenizer) { - for t.curr_rune == ' ' || - t.curr_rune == '\t' || - t.curr_rune == '\n' || - t.curr_rune == '\r' { - advance_to_next_rune(t); - } -} -scan_escape :: proc(t: ^Tokenizer, quote: rune) -> bool { - advance_to_next_rune(t); - - r := t.curr_rune; - match r { - case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote: - advance_to_next_rune(t); - return true; - default: - if t.curr_rune < 0 { - tokenizer_err(t, "Escape sequence was not terminated"); - } else { - tokenizer_err(t, "Unknown espace sequence"); - } - return false; - } -} - -next_token :: proc(t: ^Tokenizer) -> Token { - using Token_Kind; - skip_whitespace(t); - - token := Token{ - line = t.line, - column = t.curr-t.line+1, - }; - - prev := t.curr; - curr_rune := t.curr_rune; - if is_letter(cast(byte)t.curr_rune) { - for is_letter(cast(byte)t.curr_rune) || is_digit(cast(byte)t.curr_rune) { - advance_to_next_rune(t); - } - - token.text = cast(string)t.data[prev..t.curr]; - token.kind = IDENT; - } else { - advance_to_next_rune(t); - token.text = cast(string)t.data[prev..t.curr]; - match curr_rune { - default: token.kind = OTHER; - case utf8.RUNE_EOF: token.kind = EOF; - - case '/': - if t.curr_rune != '/' { - token.kind = OTHER; - } else { - token.text = ""; - for t.curr_rune != '\n' && t.curr_rune != utf8.RUNE_EOF { - advance_to_next_rune(t); - } - if t.curr_rune == utf8.RUNE_EOF { - token.kind = EOF; - token.text = cast(string)t.data[t.curr..t.curr+1]; - return token; - } - return next_token(t); - } - - - - case '=': token.kind = EQUAL; - case ',': token.kind = COMMA; - case ';': token.kind = SEMICOLON; - - case '"': - for { - r := t.curr_rune; - if r == '\n' || r < 0 { - tokenizer_err(t, "String literal not terminated"); - break; - } - advance_to_next_rune(t); - if r == '"' { - break; - } - if r == '\\' { - scan_escape(t, '"'); - } - } - - token.text = cast(string)t.data[prev+1..t.curr-1]; - token.kind = STRING; - } - } - - return token; -} - -expect_token :: proc(t: ^Tokenizer, kind: Token_Kind) -> Token { - tok := next_token(t); - if tok.kind != kind { - tokenizer_err(t, "Expected %s, got %s", kind, tok.kind); - } - return tok; -} - - -alloc_command_line_arguments :: proc() -> []string { - arg_count: i32; - arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), ^arg_count); - arg_list := new_slice(string, arg_count); - for _, i in arg_list { - arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^); - } - return arg_list; -} - -main :: proc() { - arg_list := alloc_command_line_arguments(); - - if arg_list.count < 2 { - errorln("Expected a .metagen file"); - return; - } - if arg_list.count != 2 { - errorln("Expected only one .metagen file"); - return; - } - - filename := arg_list[1]; - { // Is extension valid? - i := filename.count-1; - for i >= 0 { - if filename[i] == '.' { - break; - } - i--; - } - if ext := filename[i..]; ext != ".metagen" { - errorf("Expected a .metagen file, got %s\n", filename); - return; - } - } - - data, file_ok := os.read_entire_file(filename); - if !file_ok { - errorf("Unable to read file %s\n", filename); - return; - } - - tokenizer := Tokenizer{ - data = data, - filename = filename, - line_count = 1, - }; - t := ^tokenizer; - advance_to_next_rune(t); - if t.curr_rune == utf8.RUNE_BOM { - advance_to_next_rune(t); - } - - type: string; - prefix: string; - string_prefix: string; - settings_done := false; - - for !settings_done { - using Token_Kind; - token := next_token(t); - if token.kind == Token_Kind.EOF { - break; - } - if token.kind != IDENT { - tokenizer_err(t, "Expected an identifer"); - continue; - } - match token.text { - case "type", "prefix", "string": - default: - tokenizer_err(t, "Unknown setting %s", token.text); - } - - eq := expect_token(t, EQUAL); - ident := expect_token(t, IDENT); - match token.text { - case "type": type = ident.text; - case "prefix": prefix = ident.text; - case "string": string_prefix = ident.text; - } - - expect_token(t, SEMICOLON); - if type != "" && prefix != "" && string_prefix != "" { - settings_done = true; - } - } - - if !settings_done { - errorln("Incomplete metagen settings"); - return; - } - - - new_filename := output_filename(filename); - - file, file_err := os.open(new_filename, os.O_CREAT|os.O_TRUNC, 0); - if file_err != os.ERROR_NONE { - errorf("Unable to create file %s\n", new_filename); - return; - } - defer os.close(file); - - match type { - case "enum": - Meta_Enum :: struct { - name: string, - comment: string, - } - enums: [dynamic]Meta_Enum; - - for { - using Token_Kind; - ident := next_token(t); - if ident.kind == EOF { - break; - } - if ident.kind != IDENT { - tokenizer_err(t, "Expected an identifer, got %s", ident.text); - } - expect_token(t, COMMA); - comment := expect_token(t, STRING); - expect_token(t, SEMICOLON); - - match ident.text { - case "Kind", "COUNT": - tokenizer_err(t, "A tag cannot be called %s", ident.text); - continue; - } - - append(enums, Meta_Enum{name = ident.text, comment = comment.text}); - } - - if t.error_count > 0 { - return; - } - - fmt.fprintf(file, "typedef enum %sKind %sKind;\n", prefix, prefix); - fmt.fprintf(file, "enum %sKind {\n", prefix); - for e in enums { - fmt.fprintf(file, "\t%s_%s,\n", prefix, e.name); - } - fmt.fprintf(file, "\t%s_COUNT\n", prefix); - fmt.fprintf(file, "};\n"); - - fmt.fprintf(file, "String const %s_strings[] = {\n", string_prefix); - for e, i in enums { - fmt.fprintf(file, "\t{\"%s\", %d}", e.comment, e.comment.count); - if i+1 < enums.count { - fmt.fprint(file, ","); - } - fmt.fprintln(file, ); - } - fmt.fprintf(file, "};\n\n"); - - case "union": - Meta_Union :: struct { - name: string, - comment: string, - type: string, - } - - unions: [dynamic]Meta_Union; - - for { - using Token_Kind; - ident := next_token(t); - if ident.kind == EOF { - break; - } - if ident.kind != IDENT { - tokenizer_err(t, "Expected an identifer, got %s", ident.text); - } - expect_token(t, COMMA); - comment_string := expect_token(t, STRING); - expect_token(t, COMMA); - - brace_level := 0; - start := next_token(t); - curr := start; - ok := true; - for ok && (curr.kind != SEMICOLON || brace_level > 0) { - curr = next_token(t); - match curr.kind { - case EOF: - ok = false; - case OTHER: - match curr.text { - case "{": brace_level++; - case "}": brace_level--; - } - } - } - - name := ident.text; - - if name == "" { - continue; - } - - - if name == "Kind" { - tokenizer_err(t, "A tag cannot be called Kind"); - continue; - } - - comment := comment_string.text; - if comment != "" && comment[0] == '_' { - comment = ""; - } - - type := start.text; - type.count = curr.text.data - type.data; - type = trim(type); - - append(unions, Meta_Union{name = name, comment = comment, type = type}); - } - - if t.error_count > 0 { - return; - } - - fmt.fprintf(file, "typedef enum %sKind %sKind;\n", prefix, prefix); - fmt.fprintf(file, "enum %sKind {\n", prefix); - for u in unions { - if u.name[0] != '_' { - fmt.fprintf(file, "\t"); - } - fmt.fprintf(file, "%s_%s,\n", prefix, u.name); - } - fmt.fprintf(file, "\t%s_COUNT\n", prefix); - fmt.fprintf(file, "};\n"); - - fmt.fprintf(file, "String const %s_strings[] = {\n", string_prefix); - for u, i in unions { - fmt.fprintf(file, "\t{\"%s\", %d}", u.comment, u.comment.count); - if i+1 < unions.count { - fmt.fprint(file, ","); - } - fmt.fprintln(file, ); - } - fmt.fprintf(file, "};\n\n"); - - - for u, i in unions { - fmt.fprintf(file, "typedef %s %s%s;\n", u.type, prefix, u.name); - } - - fmt.fprintf(file, "\n\n"); - fmt.fprintf(file, "struct %s{\n", prefix); - fmt.fprintf(file, "\t%sKind kind;\n", prefix); - fmt.fprintf(file, "\tunion {\n",); - for u, i in unions { - fmt.fprintf(file, "\t\t%s%s %s;\n", prefix, u.name, u.name); - } - fmt.fprintf(file, "\t};\n"); - fmt.fprintf(file, "};\n\n"); - - fmt.fprintf(file, -` -#define %s(n_, Kind_, node_) GB_JOIN2(%s, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(%s_, Kind_)) -#define case_%s(n_, Kind_, node_) case GB_JOIN2(%s_, Kind_): { %s(n_, Kind_, node_); -#ifndef case_end -#define case_end } break; -#endif -`, - prefix, prefix, prefix, prefix, prefix); - - fmt.fprintf(file, "\n\n"); - - return; - - default: - errorf("%s is not a valid type for metagen\n", type); - return; - } - -}