Files
armorpaint/base/tools/amake/alang.ts
T
2025-03-25 18:54:13 +01:00

1742 lines
40 KiB
TypeScript

///include <stdio.h>
let alang_output: string = null;
let alang_source: string = null;
let alang_source_length: i32;
// ██████╗ █████╗ ██████╗ ███████╗ ███████╗
// ██╔══██╗ ██╔══██╗ ██╔══██╗ ██╔════╝ ██╔════╝
// ██████╔╝ ███████║ ██████╔╝ ███████╗ █████╗
// ██╔═══╝ ██╔══██║ ██╔══██╗ ╚════██║ ██╔══╝
// ██║ ██║ ██║ ██║ ██║ ███████║ ███████╗
// ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝
let specials: string[] = [":", ";", ",", "(", ")", "[", "]", "{", "}", "<", ">", "!"];
let is_comment: bool = false;
let is_string: bool = false;
let pos: i32 = 0;
let tokens: string[] = [];
let header: string = "";
function is_alpha_numeric(code: i32): bool {
return (code > 47 && code < 58) || // 0-9
(code == 45) || // -
(code == 43) || // +
(code == 95) || // _
(code == 46) || // .
(code > 64 && code < 91) || // A-Z
(code > 96 && code < 123); // a-z
}
function is_alpha(code: i32): bool {
return (code > 96 && code < 123) || // a-z
(code > 64 && code < 91) || // A-Z
(code == 95); // _
}
function is_numeric(code: i32): bool {
return (code > 47 && code < 58); // 0-9
}
function is_white_space(code: i32): bool {
return code == 32 || // " "
code == 9 || // "\t"
code == 13 || // "\r"
code == 10; // "\n"
}
function read_token(): string {
// Skip white space
while (is_white_space(char_code_at(alang_source, pos))) {
pos++;
}
let token: string = "";
let first: bool = true;
let is_anum: bool = false;
while (pos < alang_source_length) {
let c: string = char_at(alang_source, pos);
// Comment start
if (token == "//") {
is_comment = true;
}
if (is_comment) {
token += c;
pos++;
// Comment end
if (c == "\n") {
is_comment = false;
if (starts_with(token, "///include") || starts_with(token, "///define")) {
header += "#" + substring(token, 3, token.length);
}
// Remove comments
return read_token();
}
// Prevent parsing of comments
continue;
}
// String start / end
if (c == "\"") {
// Escaped \"
let last: string = token.length > 0 ? char_at(token, token.length - 1) : "";
let last_last: string = token.length > 1 ? char_at(token, token.length - 2) : "";
let is_escaped: bool = last == "\\" && last_last != "\\";
if (!is_escaped) {
is_string = !is_string;
}
// Token end - string end
if (!is_string) {
token += c;
pos++;
break;
}
}
if (is_string) {
if (c == "\r") {
pos++;
continue;
}
token += c;
pos++;
// Prevent parsing of strings
continue;
}
// Token end
if (is_white_space(char_code_at(alang_source, pos))) {
break;
}
// Parsing an alphanumeric token
if (first) {
first = false;
is_anum = is_alpha_numeric(char_code_at(c, 0));
}
// Token end
let is_special: bool = array_index_of(specials, c) > -1;
if (is_anum && is_special) {
break;
}
// Add char to token and advance pos
token += c;
pos++;
// Token end, got special
if (is_special) {
break;
}
}
return token;
}
function alang_parse() {
pos = 0;
while (true) {
let token: string = read_token();
if (token == "=" && tokens[tokens.length - 1] == "!") { // Merge "!" and "=" into "!=" token
tokens[tokens.length - 1] = "!=";
continue;
}
if (token == "") { // No more tokens
break;
}
array_push(tokens, token);
}
}
// ██╗ ██╗ ██████╗
// ██║ ██║ ██╔══██╗
// ██║ ██║ ██████╔╝
// ██║ ██║ ██╔══██╗
// ███████╗ ██║ ██████╔╝
// ╚══════╝ ╚═╝ ╚═════╝
let fhandle: any;
let strings: string[] = [];
let tabs: i32 = 0;
let new_line: bool = false;
let basic_types: string[] = ["i8", "u8", "i16", "u16", "i32", "u32", "f32", "i64", "u64", "f64", "bool"];
let pass_by_value_types: string[] = ["vec4_t", "vec3_t", "vec2_t", "quat_t", "mat4_t", "mat3_t"];
let enums: string[] = [];
let value_types: map_t<string, string> = map_create();
type string_map_t = map_t<string, string>;
let struct_types: map_t<string, string_map_t> = map_create();
let fn_declarations: map_t<string, string>;
let fn_default_params: map_t<string, string>;
let fn_call_stack: string[] = [];
let param_pos_stack: i32[] = [];
let is_for_loop: bool = false;
let global_inits: string[] = [];
let global_ptrs: string[] = [];
type string_array_t = string[];
let acontents: string_array_t[] = [];
let fnested: i32[] = [];
let add_space_keywords: string[] = ["return", "else"];
let array_contents_depth: i32 = 0;
function handle_tabs(token: string) {
// Entering block, add tab
if (token == "{") {
tabs++;
}
else if (token == "}") {
tabs--;
}
if (is_for_loop) {
return;
}
// New line, add tabs
if (new_line) {
for (let i: i32 = 0; i < tabs; ++i) {
out("\t");
}
}
}
function handle_new_line(token: string) {
if (is_for_loop) {
return;
}
// Insert new line
new_line = token == ";" || token == "{" || token == "}";
if (new_line) {
out("\n");
}
}
function handle_spaces(token: string) {
// Add space to separate keywords
if (array_index_of(add_space_keywords, token) > -1) {
out(" ");
}
}
function get_token(off: i32 = 0): string {
if (pos + off >= 0 && pos + off < tokens.length) {
return tokens[pos + off];
}
return "";
}
function get_token_after_piece(): string {
let _pos: i32 = pos;
skip_piece();
let t: string = get_token(1);
pos = _pos;
return t;
}
function skip_until(s: string) {
while (true) {
let token: string = get_token();
if (token == s) {
break;
}
pos++;
}
}
function skip_block() { // { ... }
pos++; // {
let nested: i32 = 1;
while (true) {
let token: string = get_token();
if (token == "}") {
nested--;
if (nested == 0) {
break;
}
}
else if (token == "{") {
nested++;
}
pos++;
}
}
function function_return_type(): string {
let _pos: i32 = pos;
skip_until("{");
let t: string = get_token(-1);
if (t == "]") { // ): i32[]
pos -= 2;
}
else if (t == ">") { // ): map_t<a, b>
pos -= 5;
}
pos -= 2; // ): i32 {
let result: string = get_token() == ":" ? read_type() : "void";
pos = _pos;
return result;
}
function join_type_name(type: string, name: string): string {
map_set(value_types, name, type);
if (string_index_of(type, "(*NAME)(") > 0) { // Function pointer
return string_replace_all(type, "NAME", name);
}
return type + " " + name;
}
function read_type(): string { // Cursor at ":"
pos++;
let type: string = get_token();
if (type == "(" && get_token(1) == "(") { // (()=>void)[]
pos++;
}
if (get_token(1) == ")" && get_token(2) == "[") { // (()=>void)[]
pos++;
}
if (type == "(") { // ()=>void
let params: string = "(";
if (get_token(1) != ")") { // Has params
while (true) {
skip_until(":");
params += read_type();
pos++;
if (get_token() == ")") { // End of params
break;
}
params += ",";
}
}
else {
params += "void";
}
params += ")";
skip_until("=>");
let ret: string = read_type();
type = ret + "(*NAME)" + params;
}
if (type == "color_t") {
type = "i32";
}
else if (type == "string") {
type = "string_t";
}
if (get_token(1) == "[") { // Array
if (is_struct(type)) {
type = type + "_array_t";
}
else if (type == "bool") {
type = "u8_array_t";
}
else {
type = type + "_array_t";
}
pos += 2; // Skip "[]"
}
if (is_struct(type) && array_index_of(pass_by_value_types, type) == -1) {
type += " *";
}
if (type == "map_t *" && get_token(1) == "<") { // Map
let mv: string = get_token(4);
if (mv == "f32") {
type = "f32_" + type;
}
else if (array_index_of(basic_types, mv) > -1) {
let mk: string = get_token(2);
if (mk == "i32") {
type = "i32_i" + type;
}
else {
type = "i32_" + type;
}
}
else {
let mk: string = get_token(2);
if (mk == "i32") {
type = "any_i" + type;
}
else {
type = "any_" + type;
}
}
skip_until(">"); // Skip <a, b>
}
return type;
}
function enum_access(s: string): string {
// Turn enum_t.VALUE into enum_t_VALUE
if (string_index_of(s, "_t.") > -1) {
for (let i: i32 = 0; i < enums.length; ++i) {
let e: string = enums[i];
if (string_index_of(s, e) > -1) {
s = string_replace_all(s, ".", "_");
break;
}
}
}
return s;
}
function struct_access(s: string): string {
// Turn a.b into a->b
let dot: i32 = string_index_of(s, ".");
if (dot == -1) {
return s;
}
if (is_numeric(char_code_at(s, dot + 1))) {
return s;
}
if (string_index_of(s, "\"") > -1) {
return s;
}
if (starts_with(s, "GC_ALLOC_INIT")) {
return s;
}
// Accessing pass_by_value_types struct like vec4.x
let has_minus: bool = starts_with(s, "-");
let base: string = substring(s, has_minus ? 1 : 0, dot);
let type: string = map_get(value_types, base);
if (type != null) {
let dot_last: i32 = string_last_index_of(s, ".");
if (dot != dot_last) { // a.vec4.x
let parts: string[] = string_split(s, ".");
for (let i: i32 = 1; i < parts.length - 1; ++i) {
let struct_value_types: map_t<string, string> = map_get(struct_types, type);
if (struct_value_types != null) {
type = map_get(struct_value_types, parts[i]);
// struct my_struct -> my_struct_t
// my_struct * -> my_struct
if (starts_with(type, "struct ") && ends_with(type, " *")) {
type = substring(type, 0, type.length - 2);
type = substring(type, 7, type.length) + "_t *";
}
}
}
if (!ends_with(type, " *")) { // vec4_t
let first: string = string_replace_all(substring(s, 0, dot_last), ".", "->");
let last: string = substring(s, dot_last, s.length);
return first + last; // a->vec4.x
}
}
else if (!ends_with(type, " *")) { // vec4.x
return s;
}
}
return string_replace_all(s, ".", "->");
}
function struct_alloc(token: string, type: string = null): string {
// "= { a: b, ... }" -> GC_ALLOC_INIT
if ((get_token(-1) == "=" && token == "{" && get_token(-3) != "type") || type != null) {
if (type == null) {
// let a: b = {, a = {, a.b = {
let i: i32 = get_token(-3) == ":" ? -4 : -2;
let t: string = struct_access(get_token(i));
type = value_type(t);
if (ends_with(type, " *")) { // my_struct * -> my_struct
type = substring(type, 0, type.length - 2);
}
if (starts_with(type, "struct ")) { // struct my_struct -> my_struct_t
type = substring(type, 7, type.length) + "_t";
}
}
token = "GC_ALLOC_INIT(" + type + ", {";
pos++; // {
let ternary: i32 = 0;
while (true) {
let t: string = get_token();
if (t == "}") {
break;
}
let tc: string = get_token_c();
tc = string_ops(tc);
if (get_token(1) == ":" && !ternary) {
tc = "." + tc; // a: b -> .a = b
}
if (t == ":") {
if (ternary > 0) {
ternary--;
}
else {
tc = "=";
}
}
else if (t == "?") {
ternary++;
}
else if (t == "[" && get_token(-1) == ":") {
let member: string = get_token(-2);
let types: map_t<string, string> = map_get(struct_types, type + " *");
let content_type: string = map_get(types, member);
content_type = substring(content_type, 7, content_type.length - 8); // struct my_struct_array_t * -> my_struct
tc = array_create("[", "", content_type);
}
token += tc;
pos++;
}
// "= {}" -> "= {0}", otherwise msvc refuses to init members to zero
if (get_token(-1) == "{") {
token += "0";
}
token += get_token(); // "}"
token += ")";
tabs--;
}
return token;
}
function array_type(name: string): string {
let type: string = value_type(name);
if (type == null) {
return "any";
}
if (starts_with(type, "struct ")) { // struct u8_array * -> u8_array_t *
type = substring(type, 7, type.length - 2) + "_t *";
}
type = strip(type, 10); // Strip _array_t *
if (array_index_of(basic_types, type) > -1) { // u8
return type;
}
return "any";
}
function array_contents(type: string): string[] {
// [1, 2, ..] or [{a:b}, {a:b}, ..] or [a(), b(), ..]
let contents: string[] = acontents[array_contents_depth];
array_contents_depth++;
contents.length = 0;
let content: string = "";
while (true) {
pos++;
let token: string = get_token();
if (token == "]") {
if (content != "") {
array_push(contents, content);
}
break;
}
if (token == ",") {
array_push(contents, content);
content = "";
continue;
}
if (token == "{") {
token = struct_alloc(token, type);
tabs++; // Undo tabs-- in struct_alloc
}
if (get_token(1) == "(") {
// Function call
while (true) {
pos++;
token += get_token();
token = fill_default_params(token);
if (ends_with(token, ")")) {
break;
}
}
}
// a / b - > a / (float)b
if (token == "/") {
token = "/(float)";
}
token = struct_access(token);
token = string_ops(token);
content += token;
}
array_contents_depth--;
return contents;
}
function member_name(name: string): string {
let i: i32 = string_last_index_of(name, ".");
if (i > -1) {
name = substring(name, i + 1, name.length);
}
return name;
}
function array_create(token: string, name: string, content_type: string = null): string {
if ((get_token(-1) == "=" && token == "[") || content_type != null) {
if (name == "]") {
name = get_token(-6); // ar: i32[] = [
}
let member: string = member_name(name);
let type: string = array_type(member);
// = [], = [1, 2, ..] -> any/i32/.._array_create_from_raw
if (content_type == null) {
name = struct_access(name);
content_type = value_type(name);
if (content_type != null) {
content_type = substring(content_type, 0, content_type.length - 10);
}
}
let contents: string[] = array_contents(content_type);
token = type + "_array_create_from_raw((" + type + "[]){";
for (let i: i32 = 0; i < contents.length; ++i) {
let e: string = contents[i];
token += e + ",";
}
let len: i32 = contents.length;
token += "}," + len + ")";
}
return token;
}
function array_access(token: string): string {
// array[0] -> array->buffer[0]
if (token == "[" && get_token(-1) != "=") {
return "->buffer[";
}
return token;
}
function is_struct(type: string): bool {
// Ends with _t and is not an enum
return ends_with(type, "_t") && array_index_of(enums, type) == -1;
}
function strip(name: string, len: i32): string {
return substring(name, 0, name.length - len);
}
function strip_optional(name: string): string {
if (ends_with(name, "?")) {
return strip(name, 1); // :val? -> :val
}
return name;
}
function param_pos(): i32 {
return param_pos_stack[param_pos_stack.length - 1];
}
function param_pos_add() {
param_pos_stack[param_pos_stack.length - 1]++;
}
function fn_call(): string {
return fn_call_stack[fn_call_stack.length - 1];
}
function fill_fn_params(token: string): string {
if (token == "(") {
// Function is being called to init this variable
array_push(fn_call_stack, get_token(-1));
array_push(param_pos_stack, 0);
}
else if (token == ",") {
// Param has beed passed manually
param_pos_add();
}
else if (token == ")") {
// Fill in default parameters if needed
if (get_token(-1) != "(") {
// Param has beed passed manually
param_pos_add();
}
// If default param exists, fill it
let res: string = "";
while (true) {
let i: i32 = param_pos();
let fn: string = fn_call();
let param: string = map_get(fn_default_params, fn + i);
if (param == null) {
break;
}
if (i > 0) {
res += ",";
}
res += param;
param_pos_add();
}
array_pop(fn_call_stack);
array_pop(param_pos_stack);
token = res + token;
}
return token;
}
function get_token_c(): string {
let t: string = get_token();
t = enum_access(t);
t = struct_access(t);
t = array_access(t);
return t;
}
function fill_default_params(piece: string): string {
// fn(a, 1, ..)
if (get_token(1) == ")") {
let fn_name: string = "";
let params: i32 = 0;
let i: i32 = 0;
let nested: i32 = 1;
while (true) {
let ti: string = get_token(i);
if (ti == ")") {
nested++;
}
else if (ti == "(") {
if (get_token(i - 1) == ":") { // : ()=>void
return piece;
}
nested--;
if (nested == 0) {
fn_name = get_token(i - 1);
if (get_token(i + 1) != ")") {
params++;
}
break;
}
}
else if (ti == ",") {
params++;
}
i--;
}
while (true) {
let param: string = map_get(fn_default_params, fn_name + params);
if (param == null) {
break;
}
if (params > 0) {
piece += ",";
}
piece += param;
params++;
}
}
return piece;
}
function skip_piece(nested: i32 = 0) {
let t1: string = get_token(1);
if (t1 == "(" || t1 == "[") {
nested++;
pos++;
skip_piece(nested);
return;
}
if (t1 == ")" || t1 == "]") {
nested--;
if (nested < 0) {
return;
}
pos++;
skip_piece(nested);
return;
}
if (nested > 0) {
pos++;
skip_piece(nested);
return;
}
if (starts_with(t1, ".")) {
pos++;
skip_piece();
return;
}
}
function read_piece(nested: i32 = 0): string {
// call(a, b, c), a[b + c]
let piece: string = get_token_c();
piece = string_len(piece);
piece = map_ops(piece);
let t1: string = get_token(1);
if (t1 == "(" || t1 == "[") {
nested++;
pos++;
return piece + read_piece(nested);
}
if (t1 == ")" || t1 == "]") {
nested--;
if (nested < 0) {
return piece;
}
piece = fill_default_params(piece);
pos++;
return piece + read_piece(nested);
}
if (nested > 0) {
pos++;
return piece + read_piece(nested);
}
if (starts_with(t1, ".")) {
pos++;
return piece + read_piece();
}
return piece;
}
function string_len(token: string): string {
// "str->length" -> "string_length(str)"
if (!ends_with(token, "->length")) {
return token;
}
let base: string = strip(token, 8); // ->length
let type: string = value_type(base);
if (type == "string_t *" || type == "struct string *") {
token = "string_length(" + base + ")";
}
return token;
}
function value_type(value: string): string {
let arrow: i32 = string_index_of(value, "->");
if (arrow > -1) {
let base: string = substring(value, 0, arrow);
let type: string = map_get(value_types, base);
let member: string = substring(value, arrow + 2, value.length);
let struct_value_types: map_t<string, string> = map_get(struct_types, type);
if (struct_value_types != null) {
return map_get(struct_value_types, member);
}
}
return map_get(value_types, value);
}
function set_fn_param_types() {
pos++; // (
while (true) {
pos++;
let t: string = get_token(); // Param name, ) or ,
if (t == ")") { // Params end
break;
}
if (t == ",") { // Next param
continue;
}
pos++; // :
let type: string = read_type();
map_set(value_types, t, type);
if (get_token(1) == "=") { // Skip default value
pos += 2;
}
}
}
function number_to_string(token: string): string {
let type: string = value_type(token);
if (type == "i32") {
return "i32_to_string(" + token + ")";
}
if (type == "f32") {
return "f32_to_string(" + token + ")";
}
return token;
}
function token_is_string(token: string): bool {
let is_string: bool = starts_with(token, "\"") || value_type(token) == "string_t *";
if (!is_string) {
let _pos: i32 = pos;
skip_piece();
let t1: string = get_token(1);
if (t1 == "+" || t1 == "+=" || t1 == "==" || t1 == "!=") {
let t2: string = get_token(2);
if (starts_with(t2, "\"") || value_type(t2) == "string_t *") {
is_string = true;
}
}
pos = _pos;
}
if (is_string && get_token(2) == "null") {
is_string = false;
}
return is_string;
}
function string_add(token: string): string {
token = "string_join(" + token + ",";
pos++;
let token_b: string = read_piece();
token_b = number_to_string(token_b);
token += token_b;
token += ")";
return token;
}
function string_ops(token: string): string {
// "str" + str + 1 + ...
if (!token_is_string(token)) {
return token;
}
let first: bool = true;
while (get_token_after_piece() == "+") {
if (first) {
first = false;
token = read_piece();
token = number_to_string(token);
}
pos++;
token = string_add(token);
}
// "str" += str + str2
let p1: string = get_token_after_piece();
if (p1 == "+=") {
token = read_piece();
pos++;
token = token + "=string_join(" + token + ",";
pos++;
if (get_token_after_piece() == "+") {
let token_b: string = read_piece();
token_b = number_to_string(token_b);
while (get_token_after_piece() == "+") {
pos++;
token_b = string_add(token_b);
}
token += token_b;
}
else {
token += read_piece();
}
token += ")";
}
// str = str
else if (p1 == "=") {
let _pos: i32 = pos;
let t1: string = read_piece();
pos++;
pos++; // =
let t2: string = read_piece();
// TODO: let s: string = str;
if (get_token(1) == ";" && !starts_with(t2, "\"")) { // str = str;
token = t1 + "=string_copy(" + t2 + ")"; // Copy string
}
else {
pos = _pos;
}
}
// "str" == str
else if (p1 == "==") {
token = read_piece();
pos++;
token = "string_equals(" + token + ",";
pos++;
token += read_piece();
token += ")";
}
// "str" != str
else if (p1 == "!=") {
token = read_piece();
pos++;
token = "!string_equals(" + token + ",";
pos++;
token += read_piece();
token += ")";
}
return token;
}
function array_ops(token: string): string {
// array_push -> i32_array_push/any_array_push
if (token == "array_push") {
let value: string = get_token(2);
if (get_token(3) == "[") { // raws[i].value
let t6: string = get_token(6);
value = substring(t6, 1, t6.length); // .value
}
if (string_last_index_of(value, ".") > -1) {
value = substring(value, string_last_index_of(value, ".") + 1, value.length);
}
let type: string = array_type(value);
token = type + "_array_push";
}
// array_remove -> i32_array_remove / char_ptr_array_remove
else if (token == "array_remove") {
let value: string = get_token(2);
value = struct_access(value);
let type: string = value_type(value);
if (type != null && starts_with(type, "struct ")) { // struct u8_array * -> u8_array_t *
type = substring(type, 7, type.length - 2) + "_t *";
}
if (type == "i32_array_t *") {
token = "i32_array_remove";
}
else if (type == "string_t_array_t *") {
token = "char_ptr_array_remove";
}
}
// array_index_of -> i32_array_index_of / char_ptr_array_index_of
else if (token == "array_index_of") {
let value: string = get_token(2);
value = struct_access(value);
let type: string = value_type(value);
if (type != null && starts_with(type, "struct ")) { // struct u8_array * -> u8_array_t *
type = substring(type, 7, type.length - 2) + "_t *";
}
if (type == "i32_array_t *") {
token = "i32_array_index_of";
}
else if (type == "string_t_array_t *") {
token = "char_ptr_array_index_of";
}
else {
let fn: string = map_get(fn_declarations, value);
if (fn != null && starts_with(fn, "string_t_array_t *")) {
token = "char_ptr_array_index_of";
}
}
}
return token;
}
function map_ops(token: string): string {
if (token == "map_create") {
let t: string = value_type(get_token(-2));
if (t == "i32_map_t *") {
token = "i32_map_create";
}
else if (t == "i32_imap_t *") {
token = "i32_imap_create";
}
else if (t == "any_imap_t *") {
token = "any_imap_create";
}
else {
token = "any_map_create";
}
}
else if (token == "map_set") {
let t: string = value_type(get_token(2));
if (t == "i32_map_t *") {
token = "i32_map_set";
}
else if (t == "i32_imap_t *") {
token = "i32_imap_set";
}
else if (t == "any_imap_t *") {
token = "any_imap_set";
}
else {
token = "any_map_set";
}
}
else if (token == "map_get") {
let t: string = value_type(get_token(2));
if (t == "i32_map_t *") {
token = "i32_map_get";
}
else if (t == "i32_imap_t *") {
token = "i32_imap_get";
}
else if (t == "any_imap_t *") {
token = "any_imap_get";
}
else {
token = "any_map_get";
}
}
else if (token == "map_delete") {
let t: string = value_type(get_token(2));
if (t == "any_imap_t *") {
token = "imap_delete";
}
}
return token;
}
function _t_to_struct(type: string): string {
// type_t * -> struct type *
if (ends_with(type, "_t *") && type != "string_t *") {
let type_short: string = strip(type, 4); // _t *
if (ends_with(type_short, "_array")) { // tex_format_t_array -> i32_array
for (let i: i32 = 0; i < enums.length; ++i) {
let e: string = enums[i];
if (starts_with(type_short, e)) {
type_short = "i32_array";
break;
}
}
}
type = "struct " + type_short + " *";
}
return type;
}
// ██╗ ██╗ ██████╗ ██╗ ████████╗ ███████╗ ██████╗
// ██║ ██║ ██╔══██╗ ██║ ╚══██╔══╝ ██╔════╝ ██╔════╝
// ██║ █╗ ██║ ██████╔╝ ██║ ██║ █████╗ ██║
// ██║███╗██║ ██╔══██╗ ██║ ██║ ██╔══╝ ██║
// ╚███╔███╔╝ ██║ ██║ ██║ ██║ ███████╗ ╚██████╗
// ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═════╝
function stream_write(token: string) {
fwrite(token, 1, token.length, fhandle);
}
function string_write(token: string) {
strings[strings.length - 1] += token;
}
function null_write(token: string) {
return;
}
let out: (token: string)=>void = stream_write;
function write_enums() {
for (pos = 0; pos < tokens.length; ++pos) {
let token: string = get_token();
// Turn "enum name {}" into "typedef enum {} name;"
if (token == "enum") {
out = get_token(-1) == "declare" ? &null_write : &stream_write;
pos++;
let enum_name: string = get_token();
array_push(enums, enum_name);
out("typedef enum{\n");
pos++; // {
while (true) {
// Enum contents
pos++;
token = get_token(); // Item name
if (token == "}") { // Enum end
out("}" + enum_name + ";\n");
break;
}
out("\t" + enum_name + "_" + token);
pos++; // = or ,
token = get_token();
if (token == "=") { // Enum value
pos++; // n
token = get_token();
out("=" + token);
pos++; // ,
}
out(",\n");
}
out("\n");
continue;
}
}
out = &stream_write;
}
function write_types() {
for (pos = 0; pos < tokens.length; ++pos) {
let token: string = get_token();
// Turn "type x = {};" into "typedef struct {} x;"
// Turn "type x = y;" into "typedef x y;"
if (token == "type") {
out = get_token(-1) == "declare" ? &null_write : &stream_write;
pos++;
let struct_name: string = get_token();
let stuct_name_short: string = strip(struct_name, 2); // _t
pos++;
token = get_token(); // =
if (token != "=") {
continue;
}
pos++;
token = get_token();
// "type x = y;"
if (token != "{") {
pos--;
let type: string = read_type();
type = _t_to_struct(type);
out("typedef " + type + " " + struct_name + ";\n\n");
skip_until(";");
continue;
}
// "type x = {};"
out("typedef struct " + stuct_name_short + "{\n");
let struct_value_types: map_t<string, string> = map_create();
any_array_resize(struct_value_types.keys, 512 * 2);
any_array_resize(struct_value_types.values, 512 * 2);
map_set(struct_types, struct_name + " *", struct_value_types);
while (true) {
// Struct contents
pos++;
let name: string = get_token();
if (name == "}") { // Struct end
out("}" + struct_name + ";\n");
break;
}
// val?: i32 -> val: i32
name = strip_optional(name);
pos++;
// :
let type: string = read_type();
// type_t * -> struct type *
type = _t_to_struct(type);
// my_struct_t*(*NAME)(my_struct_t *) -> struct my_struct*(*NAME)(struct my_struct*)
if (string_index_of(type, "(*NAME)") > -1) {
let args_str: string = substring(type, string_index_of(type, ")(") + 2, string_last_index_of(type, ")"));
let args: string[] = string_split(args_str, ",");
type = substring(type, 0, string_index_of(type, "(*NAME)"));
type = _t_to_struct(type);
type += "(*NAME)(";
for (let i: i32 = 0; i < args.length; ++i) {
let arg: string = args[i];
arg = _t_to_struct(arg);
if (i > 0) {
type += ",";
}
type += arg;
}
type += ")";
}
skip_until(";");
out("\t" + join_type_name(type, name) + ";\n");
map_set(struct_value_types, name, type);
}
out("\n");
continue;
}
}
out = &stream_write;
}
function write_array_types() {
// Array structs (any_array_t -> scene_t_array_t)
let array_structs: map_t<string, string> = map_create();
any_array_resize(array_structs.keys, 256 * 2);
any_array_resize(array_structs.values, 256 * 2);
for (pos = 0; pos < tokens.length; ++pos) {
let token: string = get_token();
if (get_token(1) == "[") {
let type: string = token;
if (type == "string") {
type = "string_t";
}
if (is_struct(type)) {
if (map_get(array_structs, type) == null) {
let as: string = "typedef struct " + type + "_array{" +
type + "**buffer;int length;int capacity;}" +
type + "_array_t;";
map_set(array_structs, type, as);
}
}
pos += 2; // Skip "[]"
}
}
let keys: string[] = map_keys(array_structs);
for (let i: i32 = 0; i < keys.length; ++i) {
let as: string = map_get(array_structs, keys[i]);
out(as);
out("\n");
}
out("\n");
}
function write_fn_declarations() {
fn_declarations = map_create();
any_array_resize(fn_declarations.keys, 4096 * 2);
any_array_resize(fn_declarations.values, 4096 * 2);
fn_default_params = map_create();
any_array_resize(fn_default_params.keys, 1024 * 2);
any_array_resize(fn_default_params.values, 1024 * 2);
let last_fn_name: string = "";
for (pos = 0; pos < tokens.length; ++pos) {
let token: string = get_token();
if (token == "function") {
out = get_token(-1) == "declare" ? &null_write : &stream_write;
// Return type + name
pos++;
let fn_name: string = get_token();
let ret: string = function_return_type();
if (fn_name == "(") { // Anonymous function
pos--;
fn_name = last_fn_name + "_" + pos;
}
else {
if (fn_name == "main") {
fn_name = "_main";
}
last_fn_name = fn_name;
}
// Params
let _param_pos: i32 = 0;
let params: string = "(";
pos++; // (
while (true) {
pos++;
token = get_token(); // Param name, ) or ,
if (token == ")") { // Params end
break;
}
if (token == ",") { // Next param
params += ",";
_param_pos++;
continue;
}
pos++; // :
let type: string = read_type();
params += join_type_name(type, token);
// Store param default
if (get_token(1) == "=") {
let param: string = get_token(2);
param = enum_access(param);
map_set(fn_default_params, fn_name + _param_pos, param);
pos += 2;
}
}
params += ")";
let fn_decl: string = ret + " " + fn_name + params;
out(fn_decl + ";\n");
map_set(fn_declarations, fn_name, fn_decl);
}
}
out("\n");
out = &stream_write;
}
function write_globals() {
for (pos = 0; pos < tokens.length; ++pos) {
let token: string = get_token();
if (token == "let") {
out = get_token(-1) == "declare" ? &null_write : &stream_write;
pos++;
let name: string = get_token();
pos++; // :
let type: string = read_type();
if (array_index_of(basic_types, type) == -1 &&
array_index_of(pass_by_value_types, type) == -1 &&
array_index_of(enums, type) == -1) {
array_push(global_ptrs, name);
}
out(join_type_name(type, name) + ";");
// Init this var in _kickstart()
let is_initialized: bool = get_token(1) == "=";
if (is_initialized) {
let init: string = name;
tabs = 1;
while (true) {
pos++;
token = get_token();
token = enum_access(token);
token = array_access(token);
token = struct_alloc(token);
if (token == ";") {
break;
}
token = fill_fn_params(token);
// [] -> _array_create
token = array_create(token, name);
if (token == "map_create") {
let t: string = value_type(name);
if (t == "i32_map_t *") {
token = "i32_map_create";
}
else if (t == "i32_imap_t *") {
token = "i32_imap_create";
}
else if (t == "any_imap_t *") {
token = "any_imap_create";
}
else {
token = "any_map_create";
}
}
init += token;
}
array_push(global_inits, init);
}
out("\n");
}
// Skip function blocks
if (token == "function" && get_token(-1) != "declare") {
skip_until("{");
skip_block();
}
}
out = &stream_write;
}
function write_kickstart() {
// Start function
out("\nvoid _kickstart() {\n");
// Init globals
for (let i: i32 = 0; i < global_inits.length; ++i) {
let val: string = global_inits[i];
out("\t");
let name: string = string_split(val, "=")[0];
let global_alloc: bool = array_index_of(global_ptrs, name) > -1 && !ends_with(val, "=null") && !ends_with(val, "\"");
if (global_alloc) {
out("gc_unroot(" + name + ");");
}
out(val + ";");
if (global_alloc) {
out("gc_root(" + name + ");");
}
out("\n");
}
out("\t_main();\n");
out("\t#ifndef NO_IRON_START\n");
out("\tiron_start();\n");
out("\t#endif\n");
out("}\n\n");
}
function write_function() {
// Function declaration
let fn_name: string = get_token();
if (fn_name == "main") {
fn_name = "_main";
}
let fn_decl: string = map_get(fn_declarations, fn_name);
out(fn_decl + "{\n");
// Function body
// Re-set function param types into value_types map
set_fn_param_types();
skip_until("{");
tabs = 1;
new_line = true;
let mark_as_root: string = null;
fnested.length = 0;
while (true) {
pos++;
let token: string = get_token();
if (token == "function") { // Begin nested function
let anon_fn: string = fn_name + "_" + pos;
out("&" + anon_fn);
if (get_token(-1) == "=") {
out(";\n");
}
skip_until("{");
out = &string_write;
array_push(strings, "");
let fn_decl: string = map_get(fn_declarations, anon_fn);
out(fn_decl + "{\n");
let find: string = substring(fn_decl, 0, string_index_of(fn_decl, "("));
let fn_pos: i32 = parse_int(substring(find, string_last_index_of(find, "_") + 1, find.length));
let _pos: i32 = pos;
pos = fn_pos;
set_fn_param_types();
pos = _pos;
array_push(fnested, 1);
continue;
}
if (fnested.length > 0) {
if (token == "{") {
fnested[fnested.length - 1]++;
}
else if (token == "}") { // End fnested function
fnested[fnested.length - 1]--;
if (fnested[fnested.length - 1] == 0) {
array_pop(fnested);
out("}\n\n");
if (strings.length > 1) {
let s: string = array_pop(strings);
strings[strings.length - 1] = s + strings[strings.length - 1];
}
if (fnested.length == 0) {
out = &stream_write;
}
continue;
}
}
}
// Function end
if (token == "}" && tabs == 1) {
out("}\n\n");
break;
}
handle_tabs(token);
if (token == "for") { // Skip new lines in for (;;) loop
is_for_loop = true;
}
else if (token == "{") {
is_for_loop = false;
}
// Write type and name
else if (token == "let") {
pos++;
let name: string = get_token();
pos++; // :
let type: string = read_type();
out(join_type_name(type, name));
// = or ;
pos++;
token = get_token();
}
// Turn val.a into val_a or val->a
token = enum_access(token);
token = struct_access(token);
// array[0] -> array->buffer[0]
token = array_access(token);
// Use static alloc for global pointers
let is_assign: bool = get_token(1) == "=" || get_token(1) == "+="; // += for string_join
if (is_assign && token != ":") {
if (array_index_of(global_ptrs, token) > -1) {
out("gc_unroot(" + token + ");");
if (get_token(2) != "null") {
mark_as_root = token;
}
}
}
if (token == ";" && mark_as_root != null) {
out(";gc_root(" + mark_as_root + ")");
mark_as_root = null;
}
// "= { ... }" -> GC_ALLOC_INIT
token = struct_alloc(token);
// [] -> _array_create
token = array_create(token, get_token(-2));
token = array_ops(token);
// Maps
token = map_ops(token);
// Strings
token = string_ops(token);
// "str->length" -> "string_length(str)"
token = string_len(token);
// a / b - > a / (float)b
if (token == "/") {
token = "/(float)";
}
// a(1) -> a(1, 2) // a(x: i32, y: i32 = 2)
token = fill_fn_params(token);
// Write token
out(token);
handle_spaces(token);
handle_new_line(token);
}
}
function write_functions() {
for (pos = 0; pos < tokens.length; ++pos) {
let token: string = get_token();
if (token == "function") {
pos++;
if (get_token(-2) == "declare") {
continue;
}
write_function();
}
// Write anonymous function body
while (strings.length > 0) {
let s: string = array_pop(strings);
out(s);
}
}
}
function write_iron_c() {
out("#include <iron.h>\n\n");
out(header);
write_enums();
write_types();
write_array_types();
write_fn_declarations();
write_globals();
write_kickstart();
write_functions();
}
// ██╗ ██╗ ██╗ ██████╗ ██╗ ██╗ ███████╗ ████████╗ █████╗ ██████╗ ████████╗
// ██║ ██╔╝ ██║ ██╔════╝ ██║ ██╔╝ ██╔════╝ ╚══██╔══╝ ██╔══██╗ ██╔══██╗ ╚══██╔══╝
// █████╔╝ ██║ ██║ █████╔╝ ███████╗ ██║ ███████║ ██████╔╝ ██║
// ██╔═██╗ ██║ ██║ ██╔═██╗ ╚════██║ ██║ ██╔══██║ ██╔══██╗ ██║
// ██║ ██╗ ██║ ╚██████╗ ██║ ██╗ ███████║ ██║ ██║ ██║ ██║ ██║ ██║
// ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
function main() {}
function alang(_alang_source: string, _alang_output: string) {
kickstart();
alang_source = (_alang_source); // () - no string copy
alang_source_length = alang_source.length;
alang_output = (_alang_output);
// HEAP_SIZE
any_array_resize(tokens, 512000);
any_array_resize(strings, 32);
any_array_resize(enums, 512);
any_array_resize(fn_call_stack, 32);
i32_array_resize(param_pos_stack, 32);
any_array_resize(global_inits, 1024);
any_array_resize(global_ptrs, 1024);
any_array_resize(acontents, 2);
acontents[0] = [];
acontents[1] = [];
any_array_resize(acontents[0], 4096);
any_array_resize(acontents[1], 4096);
i32_array_resize(fnested, 32);
any_array_resize(value_types.keys, 4096 * 2);
any_array_resize(value_types.values, 4096 * 2);
any_array_resize(struct_types.keys, 512 * 2);
any_array_resize(struct_types.values, 512 * 2);
//
alang_parse();
fhandle = fopen(alang_output, "wb");
write_iron_c();
fclose(fhandle);
}