Remove metagen.odin

This commit is contained in:
Ginger Bill
2017-08-27 23:28:20 +01:00
parent 7cb8016df3
commit 2ac33285c1
-595
View File
@@ -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;
}
}