diff options
Diffstat (limited to 'src/parse.c')
| -rw-r--r-- | src/parse.c | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/src/parse.c b/src/parse.c new file mode 100644 index 0000000..b6af245 --- /dev/null +++ b/src/parse.c @@ -0,0 +1,589 @@ +#include <math.h> +#include <limits.h> +#include <stdio.h> +#include "util/err.h" +#include "parse.h" +#include "lex.h" + +static bool is_expr(struct token *tok) +{ + return tok->type == TOKEN_NUMBER || tok->type == TOKEN_VAR || (tok->type == TOKEN_OP && op_category_tab[tok->op.type] == OPC_EXPR); +} + +static struct var *get_var(struct token *tok, struct source *src, struct registry *reg) +{ + struct var *var = nullptr; + map_find(®->vars, tok->var, &var); + if (!var) + source_error(src, tok->loc, "no such variable: $%.*s", PSTR(tok->var)); + return var; +} + +static void parse_expr(struct token *tok, struct expr *expr, struct source *src, struct registry *reg, bool has_time) +{ + expr->loc = tok->loc; + if (tok->type == TOKEN_NUMBER) { + expr->type = EXPR_NUMBER; + expr->number = tok->number; + } else if (tok->type == TOKEN_VAR) { + expr->type = EXPR_NUMBER; + struct var *var = get_var(tok, src, reg); + if (var->type != VAR_NUMBER) + source_error(src, tok->loc, "$%.*s is not a number", PSTR(tok->var)); + expr->number = var->number; + } else if (tok->type == TOKEN_OP) { + size_t n_args = tok->op.children.len; + if (tok->op.type == OP_TIME) { + if (!has_time) + source_error(src, tok->loc, "'time' not allowed in this context"); + if (n_args) + source_error(src, tok->loc, "'time' does not expect any arguments"); + expr->type = EXPR_TIME; + } else { + switch (tok->op.type) { + case OP_ADD: + case OP_MUL: + break; + case OP_MIN: + case OP_MAX: + if (n_args < 1) + source_error(src, tok->loc, "'%.*s' expects at least 1 argument", + PSTR(op_name_tab[tok->op.type])); + break; + case OP_DIV: + case OP_SUB: + if (n_args != 1 && n_args != 2) + source_error(src, tok->loc, "'%.*s' expects one or two arguments", + PSTR(op_name_tab[tok->op.type])); + break; + case OP_MOD: + if (n_args != 1) + source_error(src, tok->loc, "'%.*s' expects exactly two arguments", + PSTR(op_name_tab[tok->op.type])); + break; + case OP_CLAMP: + case OP_MIX: + if (n_args != 3) + source_error(src, tok->loc, "'%.*s' expects exactly three arguments", + PSTR(op_name_tab[tok->op.type])); + break; + case OP_ABS: + if (n_args != 1) + source_error(src, tok->loc, "'%.*s' expects exactly one argument", + PSTR(op_name_tab[tok->op.type])); + break; + default: + source_error(src, tok->op.name_loc, "not a valid math operation"); + } + expr->type = EXPR_OP; + expr->op.type = tok->op.type; + array_alloc(&expr->op.args, n_args); + for (size_t i = 0; i < n_args; i++) + parse_expr(&tok->op.children.ptr[i], &expr->op.args.ptr[i], src, reg, has_time); + } + } else { + source_error(src, tok->loc, "not a valid math expression"); + } +} + +static double parse_eval_expr(struct token *tok, struct source *src, struct registry *reg) +{ + struct expr expr; + parse_expr(tok, &expr, src, reg, false); + double num = expr_eval(&expr, 0.0); + expr_free(&expr); + return num; +} + +double parse_eval_expr_range(struct token *tok, struct source *src, struct registry *reg, double low, double high) +{ + double num = parse_eval_expr(tok, src, reg); + if (num < low) + source_error(src, tok->loc, "%f is smaller than %f", num, low); + if (num > high) + source_error(src, tok->loc, "%f is larger than %f", num, high); + return num; +} + +unsigned int parse_eval_expr_uint(struct token *tok, struct source *src, struct registry *reg) +{ + double num = round(parse_eval_expr(tok, src, reg)); + if (num < 0.0 || num > UINT_MAX) + source_error(src, tok->loc, "%f cannot be converted to an unsigned integer", num); + return (unsigned int) num; +} + +unsigned int parse_eval_expr_uint_range(struct token *tok, struct source *src, struct registry *reg, unsigned int low, unsigned int high) +{ + unsigned int num = parse_eval_expr_uint(tok, src, reg); + if (low && num < low) + source_error(src, tok->loc, "%u is smaller than %u", num, low); + if (high && num > high) + source_error(src, tok->loc, "%u is larger than %u", num, high); + return num; +} + +void parse_string(struct token *tok, str *out, struct source *src, struct registry *reg) +{ + if (tok->type == TOKEN_STRING) { + *out = tok->string; + } else if (tok->type == TOKEN_VAR) { + struct var *var = get_var(tok, src, reg); + if (var->type != VAR_STRING) + source_error(src, tok->loc, "$%.*s is not a string", PSTR(tok->var)); + *out = var->string; + } else { + source_error(src, tok->loc, "not a string"); + } +} + +void parse_string_loc(struct token *tok, struct loc_str *out, struct source *src, struct registry *reg) +{ + out->loc = tok->loc; + parse_string(tok, &out->str, src, reg); +} + +static void parse_name(struct token *tok, struct loc_str *name, struct source *src) +{ + if (tok->type != TOKEN_IDENT) + source_error(src, tok->loc, "not an identifier"); + name->loc = tok->loc; + name->str = tok->ident; +} + +void parse_seq_inst(struct token *tok, struct seq_inst *inst, struct source *src, struct registry *reg) +{ + if (is_expr(tok)) { + inst->type = SEQ_TIME; + inst->time = parse_eval_expr_range(tok, src, reg, 0.0, INFINITY); + } else if (tok->type == TOKEN_OP) { + if (tok->op.type == OP_POV) { + if (tok->op.children.len != 1) + source_error(src, tok->loc, "'pov' expects exactly one argument"); + inst->type = SEQ_POV; + parse_name(&tok->op.children.ptr[0], &inst->pov_name, src); + } else if (tok->op.type == OP_REP) { + if (tok->op.children.len < 2) + source_error(src, tok->loc, "'rep' expects at least two arguments"); + + inst->type = SEQ_REP; + + struct token *tok_times = &tok->op.children.ptr[0]; + if (is_expr(tok_times)) + inst->rep.times = parse_eval_expr_uint(tok_times, src, reg); + else if (tok_times->type == TOKEN_OP && tok_times->op.type == OP_INF) + inst->rep.times = SEQ_TIMES_INF; + else + source_error(src, tok_times->loc, "not a valid number of repetitions"); + + size_t n_child = tok->op.children.len-1; + array_alloc(&inst->rep.children, n_child); + for (size_t i = 0; i < n_child; i++) + parse_seq_inst(&tok->op.children.ptr[i+1], &inst->rep.children.ptr[i], src, reg); + } else { + source_error(src, tok->op.name_loc, "not a valid seq instruction"); + } + } else { + source_error(src, tok->loc, "not a valid seq instruction"); + } +} + +static bool is_transform_op(struct token *tok) +{ + return tok->type == TOKEN_OP && op_category_tab[tok->op.type] == OPC_TRANSFORM; +} + +static char swizzle_char[3] = { 'x', 'y', 'z' }; +static str transform_op_name_tab[] = { S("translate"), S("scale"), S("rotate") }; + +static void transform_parse_swizzle(size_t n, uint8_t swizzle[3], struct token *tok, struct source *src) +{ + char c = tok->ident.ptr[n]; + for (size_t d = 0; d < 3; d++) { + if (c != swizzle_char[d]) + continue; + for (size_t i = 0; i < n; i++) + if (swizzle[i] == d) + source_error(src, tok->loc, "'%c' occurs twice in swizzle", c); + swizzle[n] = d; + return; + } + source_error(src, tok->loc, "invalid swizzle component: '%c'", c); +} + +static void transform_check_assign(struct transform *trans, enum transform_op op, uint8_t dim, struct token *tok, struct source *src) +{ + if (trans->has_op[op][dim]) + source_error(src, tok->loc, "%.*s %c already assigned", + PSTR(transform_op_name_tab[op]), swizzle_char[dim]); + trans->has_op[op][dim] = true; +} + +static void parse_transform_op(struct token *tok, struct transform *trans, struct source *src, struct registry *reg) +{ + if (tok->type != TOKEN_OP) + source_error(src, tok->loc, "not a valid transform operator"); + enum transform_op op; + switch (tok->op.type) { + case OP_TRANSLATE: + op = TR_TRANSLATE; + break; + case OP_SCALE: + op = TR_SCALE; + break; + case OP_ROTATE: + op = TR_ROTATE; + break; + default: + source_error(src, tok->op.name_loc, "not a valid transform operator"); + } + + if (tok->op.children.len < 1) + source_error(src, tok->op.name_loc, "'%.*s' expects at least one argument", + PSTR(op_name_tab[tok->op.type])); + + size_t offset = 0; + uint8_t swizzle[3]; + size_t n_swizzle; + + struct token *tok_swizzle = &tok->op.children.ptr[0]; + if (tok_swizzle->type == TOKEN_IDENT) { + n_swizzle = tok_swizzle->ident.len; + if (n_swizzle > 3) + source_error(src, tok_swizzle->loc, "swizzle must not be longer than 3 chars"); + for (size_t i = 0; i < n_swizzle; i++) + transform_parse_swizzle(i, swizzle, tok_swizzle, src); + offset = 1; + + if (tok->op.children.len-offset != n_swizzle) { + if (n_swizzle == 1) + source_error(src, tok->loc, "expected exactly one numeric argument after swizzle"); + else if (tok->op.children.len-offset != 1) + source_error(src, tok->loc, "expected 1 or %zu numeric arguments after swizzle", n_swizzle); + } + } else { + n_swizzle = 3; + for (uint8_t i = 0; i < n_swizzle; i++) + swizzle[i] = i; + + if (tok->op.children.len != 1 && tok->op.children.len != 3) + source_error(src, tok->loc, "'%.*s' without a swizzle expects either one or three arguments", + PSTR(op_name_tab[tok->op.type])); + } + + for (size_t i = offset; i < tok->op.children.len; i++) { + uint8_t dim = swizzle[i-offset]; + transform_check_assign(trans, op, dim, tok, src); + parse_expr(&tok->op.children.ptr[i], &trans->op[op][dim], src, reg, true); + } + + if (n_swizzle > 1 && tok->op.children.len-offset == 1) { + for (uint8_t i = 1; i < n_swizzle; i++) { + transform_check_assign(trans, op, swizzle[i], tok, src); + expr_copy(&trans->op[op][swizzle[i]], &trans->op[op][swizzle[0]]); + } + } +} + +/* +static bool is_pos(struct token *tok) +{ + return tok->type == TOKEN_OP && op_category_tab[tok->op.type] == OPC_POS; +} +*/ + +static void parse_pos(struct token *tok, struct pos *pos, struct source *src, struct registry *reg) +{ + if (tok->type != TOKEN_OP) + source_error(src, tok->loc, "not a valid position"); + switch (tok->op.type) { + case OP_POS: + if (tok->op.children.len != 3) + source_error(src, tok->loc, "'pos' expects exactly three arguments"); + pos->type = POS_ABSOLUTE; + for (size_t i = 0; i < tok->op.children.len; i++) + parse_expr(&tok->op.children.ptr[i], &pos->absolute[i], src, reg, true); + break; + case OP_ATTACH: + if (tok->op.children.len != 1) + source_error(src, tok->loc, "'attach' expects exactly one argument"); + pos->type = POS_ATTACHED; + parse_name(&tok->op.children.ptr[0], &pos->attach_name, src); + break; + default: + source_error(src, tok->op.name_loc, "not a valid position"); + } +} + +static void parse_surface(size_t n_attrs, struct token attrs[n_attrs], struct surface *surf, struct source *src, struct registry *reg) +{ + surface_init(surf); + for (size_t i = 0; i < n_attrs; i++) { + struct token *tok = &attrs[i]; + if (tok->type != TOKEN_OP) + source_error(src, tok->loc, "not a valid surface attribute"); + switch (tok->op.type) { + case OP_TEXTURE: + case OP_CUBEMAP: + if (tok->op.children.len != 1) + source_error(src, tok->loc, "'%.*s' expects exactly one argument", + PSTR(op_name_tab[tok->op.type])); + if (surf->texture.present) + source_error(src, tok->loc, "texture already set"); + surf->texture.present = true; + surf->texture.data.source.type = tok->op.type == OP_CUBEMAP ? TEXTURE_CUBEMAP : TEXTURE_2D; + parse_string_loc(&tok->op.children.ptr[0], &surf->texture.data.source.path, src, reg); + break; + case OP_COLOR: + if (tok->op.children.len != 1) + source_error(src, tok->loc, "'color' expects exactly one argument"); + expr_free(&surf->color); + parse_expr(&tok->op.children.ptr[0], &surf->color, src, reg, true); + break; + default: + source_error(src, tok->op.name_loc, "not a valid surface attribute"); + } + } +} + +static void parse_node(struct token *tok, struct node *node, struct source *src, struct registry *reg) +{ + if (tok->type != TOKEN_OP) + source_error(src, tok->loc, "not a valid scene node"); + switch (tok->op.type) { + case OP_TRANSFORM: { + node->type = NODE_TRANSFORM; + memset(&node->transform.has_op, 0, sizeof node->transform.has_op); + + size_t idx = 0; + for (; idx < tok->op.children.len && is_transform_op(&tok->op.children.ptr[idx]); idx++) + parse_transform_op(&tok->op.children.ptr[idx], &node->transform, src, reg); + + array_alloc(&node->transform.children, tok->op.children.len-idx); + for (size_t i = idx; i < tok->op.children.len; i++) + parse_node(&tok->op.children.ptr[i], &node->transform.children.ptr[i-idx], src, reg); + break; + } + case OP_CAMERA: { + if (tok->op.children.len < 1) + source_error(src, tok->loc, "'camera' expects at least one argument"); + size_t idx = 0; + struct token *tok_name = &tok->op.children.ptr[idx]; + if (tok_name->type == TOKEN_IDENT) + idx++; + else + tok_name = nullptr; + if (tok->op.children.len < idx+1) + source_error(src, tok->loc, "named camera expects at least two arguments"); + node->type = NODE_CAMERA; + parse_pos(&tok->op.children.ptr[idx], &node->camera.target, src, reg); + node->camera.roll = false; + if (tok->op.children.len == idx+2) { + struct token *tok_attr = &tok->op.children.ptr[idx+1]; + if (tok_attr->type != TOKEN_OP) + source_error(src, tok_attr->loc, "not a valid camera attribute"); + if (tok_attr->op.type != OP_ROLL) + source_error(src, tok_attr->op.name_loc, "not a valid camera attribute"); + if (tok_attr->op.children.len != 0) + source_error(src, tok_attr->loc, "'roll' does not expect any arguments"); + node->camera.roll = true; + } else if (tok->op.children.len != idx+1) { + source_error(src, tok->loc, "excess camera arguments"); + } + if (tok_name) { + bool succ; + map_insert(®->cameras, tok_name->ident, &node->camera, &succ); + if (!succ) + source_error(src, tok_name->loc, "camera '%.*s' already registered", + PSTR(tok_name->ident)); + } else { + if (reg->default_camera) + source_error(src, tok->loc, "unnamed camera already registered"); + reg->default_camera = &node->camera; + } + break; + } + case OP_CUBE: + node->type = NODE_OBJECT; + node->object.mesh.source.type = MESH_CUBE; + parse_surface(tok->op.children.len, tok->op.children.ptr, &node->object.surface, src, reg); + break; + case OP_OBJ: + if (tok->op.children.len < 1) + source_error(src, tok->loc, "'obj' expects at least one argument"); + node->type = NODE_OBJECT; + node->object.mesh.source.type = MESH_OBJ; + parse_string_loc(&tok->op.children.ptr[0], &node->object.mesh.source.path, src, reg); + parse_surface(tok->op.children.len-1, tok->op.children.ptr+1, &node->object.surface, src, reg); + break; + case OP_BONE: { + node->type = NODE_BONE; + if (tok->op.children.len != 1) + source_error(src, tok->loc, "'bone' expects exactly one argument"); + struct token *tok_name = &tok->op.children.ptr[0]; + if (tok_name->type != TOKEN_IDENT) + source_error(src, tok_name->loc, "not an identifier"); + bool succ; + map_insert(®->bones, tok_name->ident, &node->bone, &succ); + if (!succ) + source_error(src, tok_name->loc, "bone '%.*s' already registered", + PSTR(tok_name->ident)); + break; + } + case OP_LIGHT: + node->type = NODE_LIGHT; + source_error(src, tok->loc, "TODO: implement light"); + break; + default: + source_error(src, tok->op.name_loc, "not a valid scene node"); + } +} + +static void parse_toplevel(struct token *tok, struct scene *scene, struct source *src, struct registry *reg) +{ + if (tok->type != TOKEN_OP) + source_error(src, tok->loc, "not allowed at top-level"); + + switch (tok->op.type) { + case OP_VAR: { + if (tok->op.children.len != 2 && tok->op.children.len != 3) + source_error(src, tok->loc, "'var' expects two or three arguments"); + + struct var var; + + struct token *tok_type = &tok->op.children.ptr[0]; + if (tok_type->type == TOKEN_IDENT && array_eq(tok_type->ident, S("string"))) + var.type = VAR_STRING; + else if (tok_type->type == TOKEN_IDENT && array_eq(tok_type->ident, S("number"))) + var.type = VAR_NUMBER; + else + source_error(src, tok_type->loc, "not a valid variable type"); + + struct token *tok_name = &tok->op.children.ptr[1]; + if (tok_name->type != TOKEN_VAR) + source_error(src, tok_name->loc, "not a variable name"); + + if (tok->op.children.len == 3) { + struct token *tok_default = &tok->op.children.ptr[2]; + switch (var.type) { + case VAR_STRING: parse_string(tok_default, &var.string, src, reg); break; + case VAR_NUMBER: var.number = parse_eval_expr(tok_default, src, reg); break; + } + } + + str *raw_var = nullptr; + map_find(®->raw_vars, tok_name->var, &raw_var); + + if (raw_var) { + switch (var.type) { + case VAR_STRING: + var.string = *raw_var; + break; + case VAR_NUMBER: + if (!str_parse_double(*raw_var, &var.number)) + eprintf("invalid value for variable $%.*s: not a number: %.*s\n", + PSTR(tok_name->var), PSTR(*raw_var)); + break; + } + } else if (tok->op.children.len != 3) { + eprintf("missing value for %.*s variable $%.*s\n", + PSTR(tok_type->ident), PSTR(tok_name->var)); + } + + bool success; + map_insert(®->vars, tok_name->var, var, &success); + if (!success) + source_error(src, tok->loc, "variable $%.*s already declared", PSTR(tok_name->var)); + break; + } + case OP_FPS: + if (tok->op.children.len != 1) + source_error(src, tok->loc, "'fps' expects exactly one argument"); + scene->fps = parse_eval_expr_uint_range(&tok->op.children.ptr[0], src, reg, 1, 0); + break; + case OP_SIZE: + if (tok->op.children.len != 2) + source_error(src, tok->loc, "'size' expects exactly two arguments"); + for (size_t i = 0; i < 2; i++) + scene->size[i] = parse_eval_expr_uint_range(&tok->op.children.ptr[i], src, reg, 1, 0); + break; + case OP_SEQ: + if (scene->seq.ptr) + source_error(src, tok->loc, "sequence already defined"); + array_alloc(&scene->seq, tok->op.children.len); + for (size_t i = 0; i < tok->op.children.len; i++) + parse_seq_inst(&tok->op.children.ptr[i], &scene->seq.ptr[i], src, reg); + break; + case OP_PROJ: { + if (tok->op.children.len < 1) + source_error(src, tok->loc, "'proj' expects at least one argument"); + + enum projection_type type; + struct token *tok_type = &tok->op.children.ptr[0]; + if (tok_type->type == TOKEN_IDENT && array_eq(tok_type->ident, S("perspective"))) + type = PROJ_PERSPECTIVE; + else if (tok_type->type == TOKEN_IDENT && array_eq(tok_type->ident, S("ortho"))) + type = PROJ_ORTHO; + else + source_error(src, tok_type->loc, "not a valid projection type"); + + proj_free(&scene->proj); + proj_default(&scene->proj, type); + + for (size_t i = 1; i < tok->op.children.len; i++) { + struct token *child = &tok->op.children.ptr[i]; + if (child->type == TOKEN_OP && child->op.type == OP_FOV) { + if (type != PROJ_PERSPECTIVE) + source_error(src, child->op.name_loc, "'fov' only valid for perspective projection"); + if (child->op.children.len != 1) + source_error(src, child->loc, "'fov' expects exactly one argument"); + expr_free(&scene->proj.perspective.fov); + parse_expr(&child->op.children.ptr[0], &scene->proj.perspective.fov, src, reg, true); + } else if (child->type == TOKEN_OP && child->op.type == OP_PLANES) { + float *planes; + switch (type) { + case PROJ_PERSPECTIVE: + if (child->op.children.len != 2) + source_error(src, child->loc, "'planes' expects exactly two arguments for perspective projection"); + planes = scene->proj.perspective.planes; + break; + case PROJ_ORTHO: + if (child->op.children.len != 6) + source_error(src, child->loc, "'planes' expects exactly six arguments for ortho projection"); + planes = scene->proj.ortho.planes; + break; + } + for (size_t i = 0; i < child->op.children.len; i++) + planes[i] = parse_eval_expr(&child->op.children.ptr[i], src, reg); + } else { + source_error(src, child->loc, "not a valid projection attribute"); + } + } + break; + } + case OP_OUT: + if (tok->op.children.len != 1) + source_error(src, tok->loc, "'out' expects exactly one argument"); + free(scene->outfile.ptr); + parse_string(&tok->op.children.ptr[0], &scene->outfile, src, reg); + break; + case OP_SCENE: + if (scene->root.transform.children.ptr) + source_error(src, tok->loc, "scene already defined"); + array_alloc(&scene->root.transform.children, tok->op.children.len); + for (size_t i = 0; i < tok->op.children.len; i++) + parse_node(&tok->op.children.ptr[i], &scene->root.transform.children.ptr[i], src, reg); + break; + default: + source_error(src, tok->op.name_loc, "not allowed at top-level"); + } +} + +void parse_scene(struct source *src, struct scene *scene, struct registry *reg) +{ + struct token tok; + while (lex_token(src, &tok)) { + parse_toplevel(&tok, scene, src, reg); + free_token(&tok); + } +} |
