353 lines
8.3 KiB
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,
|
|
}
|