gutter_runner/assetcomp/assetcomp.odin

353 lines
8.3 KiB
Odin

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,
}