package assetcomp import "common:encoding/sexp" import "common:name" import "core:container/intrusive/list" import "core:fmt" import "core:log" import os "core:os/os2" _ :: sexp main :: proc() { context.logger = log.create_console_logger() file, file_err := os.read_entire_file("src_assets/defs.ass", context.temp_allocator) if file_err != nil { log.fatalf("failed to read ass file {}", file_err) } parser: sexp.SEXP_Parser parser.data = transmute(string)file expr_list, err := sexp.parse(&parser) if err != nil { w := os.to_writer(os.stderr) sexp.print_pretty_error(w, parser.data, err) fmt.print("\n") } it := sexp.iterator_list(expr_list) for expr in sexp.iterator_next(&it) { sexp.print_sexp(expr.expr, os.to_writer(os.stdout)) fmt.print("\n") } asset_gather_ctx: Asset_Gather_Context interpreter: Lisp_Interpreter interpreter.user_data = &asset_gather_ctx interpreter.external_func_table[sexp.Ident(name.from_string("define-asset"))] = lisp_define_asset interpreter.external_func_table[sexp.Ident(name.from_string("run-proc"))] = lisp_run_proc lisp_interpret_expr_list(&interpreter, sexp.iterator_list(expr_list)) for n, local_func in interpreter.local_func_table { log.debugf("local func {}\nargs: ", name.to_string(name.Name(n))) sexp.print_list(local_func.args, os.to_writer(os.stderr)) log.debugf("\nbody: ") sexp.print_list(local_func.body, os.to_writer(os.stderr)) log.debugf("\n") } } lisp_run_proc :: proc(ctx: ^Lisp_Interpreter, args: sexp.List_Iterator) -> sexp.Sexp { log.debugf("called run-proc with args:") args := args for body in sexp.iterator_next(&args) { sexp.print_sexp(lisp_interpret_expr(ctx, body.expr), os.to_writer(os.stdout)) fmt.print("\n") } fmt.print("\n") return nil } lisp_define_asset :: proc(ctx: ^Lisp_Interpreter, args: sexp.List_Iterator) -> sexp.Sexp { // asset_ctx := cast(^Asset_Gather_Context)ctx.user_data args := args type := name.Name( lisp_interpret_expr(ctx, sexp.iterator_next_checked(&args)).(sexp.Atom).(sexp.Tag), ) id := lisp_interpret_expr(ctx, sexp.iterator_next_checked(&args)).(sexp.Atom).(string) log.debugf("called define-asset with id {}, type {}, body:", id, name.to_string(type)) for body in sexp.iterator_next(&args) { sexp.print_sexp(lisp_interpret_expr(ctx, body.expr), os.to_writer(os.stdout)) fmt.print("\n") } return nil } Lisp_External_Func :: #type proc(ctx: ^Lisp_Interpreter, args: sexp.List_Iterator) -> sexp.Sexp global_func_table: map[sexp.Ident]Lisp_External_Func @(init) init_global_funcs :: proc() { global_func_table[sexp.Ident(name.from_string("defn"))] = proc(ctx: ^Lisp_Interpreter, args: sexp.List_Iterator) -> sexp.Sexp { args := args name := sexp.to_ident_checked(sexp.iterator_next_checked(&args)) func_args := sexp.iterator_next_checked(&args).(sexp.Sexp_List) body := args ctx.local_func_table[name] = Local_Func { args = sexp.iterator_list(func_args), body = body, } return nil } global_func_table[sexp.Ident(name.from_string("def"))] = proc(ctx: ^Lisp_Interpreter, args: sexp.List_Iterator) -> sexp.Sexp { args := args name := sexp.to_ident_checked(sexp.iterator_next_checked(&args)) value := sexp.iterator_next_checked(&args) result := lisp_interpret_expr(ctx, value) get_scope(ctx).values[name] = result return result } global_func_table[sexp.Ident(name.from_string("let"))] = proc(ctx: ^Lisp_Interpreter, args: sexp.List_Iterator) -> sexp.Sexp { args := args bindings := sexp.iterator_next_checked(&args).(sexp.Sexp_List) push_scope(ctx) defer pop_scope(ctx) scope := get_scope(ctx) binds_it := sexp.iterator_list(bindings) for name_expr in sexp.iterator_next(&binds_it) { var_name := sexp.to_ident_checked(name_expr.expr) value := lisp_interpret_expr(ctx, sexp.iterator_next_checked(&binds_it)) scope.values[var_name] = value } body := args return lisp_interpret_expr_list(ctx, body) } global_func_table[sexp.Ident(name.from_string("temp-dir"))] = proc(ctx: ^Lisp_Interpreter, args: sexp.List_Iterator) -> sexp.Sexp { args := args pattern := lisp_interpret_expr(ctx, sexp.iterator_next_checked(&args)).(sexp.Atom).(string) assert(len(pattern) > 0) temp_dir, _ := os.temp_dir(context.temp_allocator) temp_path, _ := os.mkdir_temp(temp_dir, pattern, context.temp_allocator) return sexp.Atom(temp_path) } global_func_table[sexp.Ident(name.from_string("if"))] = proc(ctx: ^Lisp_Interpreter, args: sexp.List_Iterator) -> sexp.Sexp { args := args condition := lisp_interpret_expr(ctx, sexp.iterator_next_checked(&args)) true_body := sexp.iterator_next_checked(&args) if condition != nil { return lisp_interpret_expr(ctx, true_body) } else { false_body, ok := sexp.iterator_next(&args) if ok { return lisp_interpret_expr(ctx, false_body.expr) } } return nil } } Local_Func :: struct { args: sexp.List_Iterator, body: sexp.List_Iterator, } Scope :: struct { values: map[sexp.Ident]sexp.Sexp, } MAX_SCOPE_DEPTH :: 32 Lisp_Interpreter :: struct { user_data: rawptr, external_func_table: map[sexp.Ident]Lisp_External_Func, local_func_table: map[sexp.Ident]Local_Func, scope_stack: [MAX_SCOPE_DEPTH]Scope, scope_idx: i32, } push_scope :: proc(ctx: ^Lisp_Interpreter) { assert(ctx.scope_idx + 1 < len(ctx.scope_stack)) ctx.scope_idx += 1 } pop_scope :: proc(ctx: ^Lisp_Interpreter) { assert(ctx.scope_idx - 1 >= 0) delete(ctx.scope_stack[ctx.scope_idx].values) ctx.scope_idx -= 1 } get_scope :: proc(ctx: ^Lisp_Interpreter) -> ^Scope { return &ctx.scope_stack[ctx.scope_idx] } find_value_scope :: proc(ctx: ^Lisp_Interpreter, name: sexp.Ident) -> sexp.Sexp { idx := ctx.scope_idx for idx >= 0 { scope := &ctx.scope_stack[idx] value, ok := scope.values[name] if ok { return value } idx -= 1 } return nil } lisp_apply :: proc( ctx: ^Lisp_Interpreter, func: sexp.Ident, args: sexp.List_Iterator, ) -> ( result: sexp.Sexp, ) { log.debugf("apply {} {}", name.to_string(name.Name(func)), sexp.to_string_temp(args)) { local_func, ok := ctx.local_func_table[func] if ok { push_scope(ctx) defer pop_scope(ctx) scope := get_scope(ctx) def_args_it := local_func.args provided_args_it := args for defined_arg in sexp.iterator_next(&def_args_it) { var_name := sexp.to_ident_checked(defined_arg.expr) var_value := lisp_interpret_expr( ctx, sexp.iterator_next_checked(&provided_args_it), ) log.debugf( "adding local var to scope {} {}", name.to_string(name.Name(var_name)), sexp.to_string_temp(var_value), ) scope.values[var_name] = lisp_interpret_expr(ctx, var_value) } body_it := local_func.body for body in sexp.iterator_next(&body_it) { // Last one will be the actual result result = lisp_interpret_expr(ctx, body.expr) } return } } { external_func, ok := ctx.external_func_table[func] if ok { return external_func(ctx, args) } } { global_func, ok := global_func_table[func] if ok { return global_func(ctx, args) } } log.fatalf("function with name {} not found", name.to_string(name.Name(func))) return } lisp_interpret_expr_list :: proc( ctx: ^Lisp_Interpreter, body: sexp.List_Iterator, ) -> ( result: sexp.Sexp, ) { body := body for expr in sexp.iterator_next(&body) { result = lisp_interpret_expr(ctx, expr.expr) } return } lisp_interpret_expr :: proc(ctx: ^Lisp_Interpreter, expr: sexp.Sexp) -> (result: sexp.Sexp) { for { switch &s in expr { case sexp.Atom: #partial switch a in s { case sexp.Ident: result = find_value_scope(ctx, a) case: result = s } case sexp.Sexp_List: if list.is_empty(cast(^list.List)&s) { return nil } it := sexp.iterator_list(s) raw_func_expr := sexp.iterator_next_checked(&it) func_ident, ok := sexp.to_ident(raw_func_expr) if !ok { func_expr := lisp_interpret_expr(ctx, raw_func_expr) func_ident, ok = sexp.to_ident(func_expr) } if !ok { log.fatalf("expected identifier in a call expression, got {}", func_ident) } result = lisp_apply(ctx, func_ident, it) } if result == nil || !sexp.is_ident_expr(result) { break } } return } Asset_Model :: struct { // Expression that defines a model model: sexp.Sexp, } Asset_Union :: union { Asset_Model, } Asset_Def :: struct { data: Asset_Union, } Asset_Gather_Context :: struct { assets: map[string]Asset_Def, }