Add native RND and ASC functions
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 40s
akbasic CI Build / sanitizers (push) Failing after 45s
akbasic CI Build / coverage (push) Failing after 48s
akbasic CI Build / akgl_build (push) Failing after 49s
akbasic CI Build / mutation_test (push) Failing after 45s
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 40s
akbasic CI Build / sanitizers (push) Failing after 45s
akbasic CI Build / coverage (push) Failing after 48s
akbasic CI Build / akgl_build (push) Failing after 49s
akbasic CI Build / mutation_test (push) Failing after 45s
Implement bounded random integers with lazy clock seeding and add ASC as the inverse of CHR. Cover dispatch, validation, deterministic LCG output, UTF-8 round trips, function reference, and the breakout tutorial. Closes #16. Co-authored-by: andrew <andrew@aklabs.net>
This commit is contained in:
@@ -183,6 +183,69 @@ akerr_ErrorContext *akbasic_fn_chr(akbasic_Runtime *obj, akbasic_ASTLeaf *expr,
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *akbasic_fn_asc(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
akbasic_Value *arg = NULL;
|
||||
akbasic_Value *out = NULL;
|
||||
const unsigned char *text = NULL;
|
||||
int64_t codepoint = 0;
|
||||
|
||||
(void)lval; (void)rval;
|
||||
PASS(errctx, first_arg(obj, expr, "ASC", NULL, &arg, &out));
|
||||
FAIL_NONZERO_RETURN(errctx, (arg->valuetype != AKBASIC_TYPE_STRING), AKBASIC_ERR_TYPE,
|
||||
"ASC expected a string");
|
||||
FAIL_ZERO_RETURN(errctx, (arg->stringval[0] != '\0'), AKBASIC_ERR_BOUNDS,
|
||||
"ASC expected a non-empty string");
|
||||
|
||||
/* Decode the first UTF-8 code point, the inverse of CHR's encoder. */
|
||||
text = (const unsigned char *)arg->stringval;
|
||||
if ( text[0] < 0x80 ) {
|
||||
codepoint = text[0];
|
||||
} else if ( (text[0] & 0xE0) == 0xC0 ) {
|
||||
codepoint = ((int64_t)(text[0] & 0x1F) << 6) |
|
||||
(text[1] & 0x3F);
|
||||
} else if ( (text[0] & 0xF0) == 0xE0 ) {
|
||||
codepoint = ((int64_t)(text[0] & 0x0F) << 12) |
|
||||
((int64_t)(text[1] & 0x3F) << 6) |
|
||||
(text[2] & 0x3F);
|
||||
} else {
|
||||
codepoint = ((int64_t)(text[0] & 0x07) << 18) |
|
||||
((int64_t)(text[1] & 0x3F) << 12) |
|
||||
((int64_t)(text[2] & 0x3F) << 6) |
|
||||
(text[3] & 0x3F);
|
||||
}
|
||||
out->valuetype = AKBASIC_TYPE_INTEGER;
|
||||
out->intval = codepoint;
|
||||
*dest = out;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *akbasic_fn_rnd(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
akbasic_Value *arg = NULL;
|
||||
akbasic_Value *out = NULL;
|
||||
const int64_t modulus = 2147483648;
|
||||
|
||||
(void)lval; (void)rval;
|
||||
PASS(errctx, first_arg(obj, expr, "RND", NULL, &arg, &out));
|
||||
FAIL_NONZERO_RETURN(errctx, (arg->valuetype != AKBASIC_TYPE_INTEGER), AKBASIC_ERR_TYPE,
|
||||
"RND expected an integer");
|
||||
FAIL_ZERO_RETURN(errctx, (arg->intval > 0), AKBASIC_ERR_VALUE,
|
||||
"RND count %" PRId64 " must be positive", arg->intval);
|
||||
|
||||
if ( !obj->rndseeded ) {
|
||||
obj->rndseed = obj->timems % modulus;
|
||||
obj->rndseeded = true;
|
||||
}
|
||||
obj->rndseed = (obj->rndseed * 1103515245 + 12345) % modulus;
|
||||
out->valuetype = AKBASIC_TYPE_INTEGER;
|
||||
out->intval = (obj->rndseed / 65536) % arg->intval;
|
||||
*dest = out;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *akbasic_fn_hex(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
|
||||
@@ -37,6 +37,7 @@ static const akbasic_Verb VERBS[] = {
|
||||
{ "ABS", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_abs },
|
||||
{ "AND", AKBASIC_TOK_AND, -1, NULL, NULL },
|
||||
{ "APPEND", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_append },
|
||||
{ "ASC", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_asc },
|
||||
{ "ATN", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_atn },
|
||||
{ "AUTO", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_auto },
|
||||
{ "BACKUP", AKBASIC_TOK_COMMAND, -1, akbasic_parse_optional_arglist, akbasic_cmd_backup },
|
||||
@@ -146,6 +147,7 @@ static const akbasic_Verb VERBS[] = {
|
||||
{ "RGR", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_rgr },
|
||||
{ "RIGHT", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_right },
|
||||
{ "RMENU", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_rmenu },
|
||||
{ "RND", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_rnd },
|
||||
{ "RSPCOLOR", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_rspcolor },
|
||||
{ "RSPHIT", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_rsphit },
|
||||
{ "RSPPOS", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_rsppos },
|
||||
|
||||
@@ -140,6 +140,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akbasic_cmd_stop(struct akbasic_Runtime *obj,
|
||||
|
||||
/* Function handlers -- src/runtime_functions.c */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_abs(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_asc(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_atn(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_chr(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_cos(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
@@ -154,6 +155,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_peek(struct akbasic_Runtime *obj,
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_pointer(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_pointervar(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_rad(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_rnd(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_right(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_sgn(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_fn_shl(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest);
|
||||
|
||||
Reference in New Issue
Block a user