/** * @file runtime_format.c * @brief The group D verbs: PRINT USING, PUDEF, WIDTH and CHAR. * * Formatting and text placement. The field rendering itself lives in * src/format.c, which is a pure function of a format string and a value and is * tested as one; what is here is the verbs that reach it. */ #include #include #include #include #include #include #include #include "verbs.h" /* Most verbs answer "did something happen"; this is that answer. */ #define SUCCEED_TRUE(__obj, __dest) \ do { \ *(__dest) = &(__obj)->staticTrueValue; \ } while ( 0 ) /* ------------------------------------------------------------------ PUDEF -- */ akerr_ErrorContext *akbasic_cmd_pudef(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest) { PREPARE_ERROR(errctx); akbasic_Value *value = NULL; (void)lval; (void)rval; FAIL_ZERO_RETURN(errctx, (obj != NULL && dest != NULL), AKERR_NULLPOINTER, "NULL argument in PUDEF"); FAIL_ZERO_RETURN(errctx, (expr != NULL && expr->right != NULL), AKBASIC_ERR_SYNTAX, "Expected PUDEF \"characters\""); PASS(errctx, akbasic_runtime_evaluate(obj, expr->right, &value)); FAIL_NONZERO_RETURN(errctx, (value->valuetype != AKBASIC_TYPE_STRING), AKBASIC_ERR_TYPE, "PUDEF expected a string"); /* * No device needed. The fill characters are the program's state, the same * way COLOR's source bindings are, and a runtime with no output device is * still entitled to remember what it was told. */ PASS(errctx, akbasic_format_pudef(&obj->format_state, value->stringval)); SUCCEED_TRUE(obj, dest); SUCCEED_RETURN(errctx); } /* ------------------------------------------------------------------ WIDTH -- */ akerr_ErrorContext *akbasic_cmd_width(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest) { PREPARE_ERROR(errctx); double args[1]; int count = 0; (void)lval; (void)rval; FAIL_ZERO_RETURN(errctx, (obj != NULL && dest != NULL), AKERR_NULLPOINTER, "NULL argument in WIDTH"); PASS(errctx, akbasic_args_numbers(obj, expr, "WIDTH", args, 1, &count)); FAIL_ZERO_RETURN(errctx, (count == 1), AKBASIC_ERR_SYNTAX, "Expected WIDTH (1 or 2)"); /* * BASIC 7.0's WIDTH sets the thickness of the lines the *graphics* verbs * draw, not a text column count -- a common confusion, and the reason this * verb lives with the drawing state rather than with PRINT. */ FAIL_ZERO_RETURN(errctx, (args[0] == 1.0 || args[0] == 2.0), AKBASIC_ERR_BOUNDS, "WIDTH is 1 or 2, not %d", (int)args[0]); obj->gfx.linewidth = (int)args[0]; SUCCEED_TRUE(obj, dest); SUCCEED_RETURN(errctx); } /* ------------------------------------------------------------------- CHAR -- */ akerr_ErrorContext *akbasic_cmd_char(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest) { PREPARE_ERROR(errctx); akbasic_ASTLeaf *arg = NULL; akbasic_Value *value = NULL; char text[AKBASIC_MAX_STRING_LENGTH]; double coords[3]; int count = 0; (void)lval; (void)rval; FAIL_ZERO_RETURN(errctx, (obj != NULL && dest != NULL), AKERR_NULLPOINTER, "NULL argument in CHAR"); FAIL_ZERO_RETURN(errctx, (obj->sink != NULL), AKBASIC_ERR_DEVICE, "CHAR needs a text device and this runtime has none"); FAIL_ZERO_RETURN(errctx, (obj->sink->moveto != NULL), AKBASIC_ERR_DEVICE, "CHAR needs a text device that can position its cursor, and this one cannot"); /* * CHAR colour, column, row, "text". The colour argument is accepted and * ignored: this interpreter's text sink draws in one colour, chosen by the * host when it built the sink, and a per-call colour would have to become * part of the sink interface. Recorded in TODO.md section 5. */ arg = akbasic_leaf_first_argument(expr); for ( count = 0; count < 3 && arg != NULL; count++, arg = arg->next ) { PASS(errctx, akbasic_runtime_evaluate(obj, arg, &value)); FAIL_NONZERO_RETURN(errctx, (value->valuetype == AKBASIC_TYPE_STRING), AKBASIC_ERR_TYPE, "CHAR expected a number in argument %d", count + 1); coords[count] = (value->valuetype == AKBASIC_TYPE_FLOAT) ? value->floatval : (double)value->intval; } FAIL_ZERO_RETURN(errctx, (count == 3 && arg != NULL), AKBASIC_ERR_SYNTAX, "Expected CHAR (colour), (column), (row), (string)"); PASS(errctx, akbasic_runtime_evaluate(obj, arg, &value)); PASS(errctx, akbasic_value_to_string(value, text, sizeof(text))); PASS(errctx, obj->sink->moveto(obj->sink, (int)coords[1], (int)coords[2])); PASS(errctx, obj->sink->write(obj->sink, text)); SUCCEED_TRUE(obj, dest); SUCCEED_RETURN(errctx); }