mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-09 13:01:38 -07:00
Remove metagen.odin
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user