void sl_init_regexp(sl_vm_t* vm) { vm->lib.Regexp = sl_define_class(vm, "Regexp", vm->lib.Object); sl_class_set_allocator(vm, vm->lib.Regexp, allocate_regexp); vm->lib.Regexp_Match = sl_define_class3(vm, sl_intern(vm, "Match"), vm->lib.Object, vm->lib.Regexp); sl_class_set_allocator(vm, vm->lib.Regexp_Match, allocate_regexp_match); sl_define_method(vm, vm->lib.Regexp, "init", -2, sl_regexp_init); /* sl_define_method(vm, vm->lib.Regexp, "compile", 0, sl_regexp_compile); */ sl_define_method(vm, vm->lib.Regexp, "match", -2, sl_regexp_match); sl_define_method(vm, vm->lib.Regexp, "source", 0, sl_regexp_source); sl_define_method(vm, vm->lib.Regexp, "options", 0, sl_regexp_options); sl_define_method(vm, vm->lib.Regexp, "==", 1, sl_regexp_eq); sl_class_set_const(vm, vm->lib.Regexp, "CASELESS", sl_make_int(vm, PCRE_CASELESS)); sl_class_set_const(vm, vm->lib.Regexp, "EXTENDED", sl_make_int(vm, PCRE_EXTENDED)); sl_class_set_const(vm, vm->lib.Regexp, "PCRE_VERSION", sl_make_cstring(vm, pcre_version())); sl_define_method(vm, vm->lib.Regexp_Match, "regexp", 0, sl_regexp_match_regexp); sl_define_method(vm, vm->lib.Regexp_Match, "[]", 1, sl_regexp_match_index); sl_define_method(vm, vm->lib.Regexp_Match, "byte_offset", 1, sl_regexp_match_byte_offset); sl_define_method(vm, vm->lib.Regexp_Match, "offset", 1, sl_regexp_match_offset); sl_define_method(vm, vm->lib.Regexp_Match, "capture", 1, sl_regexp_match_capture); sl_define_method(vm, vm->lib.Regexp_Match, "length", 0, sl_regexp_match_length); sl_define_method(vm, vm->lib.Regexp_Match, "before", 0, sl_regexp_match_before); sl_define_method(vm, vm->lib.Regexp_Match, "after", 0, sl_regexp_match_after); }
SLVAL sl_regexp_match_capture(sl_vm_t* vm, SLVAL self, SLVAL i) { sl_regexp_match_t* match = get_regexp_match(vm, self); int index = cap_index(vm, self, i); if(index < 0) { return vm->lib.nil; } int start = sl_string_index_for_byte_offset(vm, match->match_string, match->captures[index]); int end = sl_string_index_for_byte_offset(vm, match->match_string, match->captures[index + 1]); SLVAL off_len[] = { sl_make_int(vm, start), sl_make_int(vm, end - start) }; return sl_make_array(vm, 2, off_len); }
SLVAL sl_regexp_match_after(sl_vm_t* vm, SLVAL self) { sl_regexp_match_t* match = get_regexp_match(vm, self); int index = cap_index(vm, self, sl_make_int(vm, 0)); sl_string_t* str = (sl_string_t*)sl_get_ptr(match->match_string); return sl_make_string(vm, str->buff + match->captures[index + 1], str->buff_len - match->captures[index + 1]); }
static SLVAL response_redirect(sl_vm_t* vm, SLVAL self, SLVAL url) { response(vm)->status = 302; response_set_header(vm, self, sl_make_cstring(vm, "Location"), url); sl_exit(vm, sl_make_int(vm, 0)); return self; /* never reached */ }
static SLVAL sl_method_arity(sl_vm_t* vm, SLVAL method) { sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); if(!(methp->base.user_flags & SL_FLAG_METHOD_INITIALIZED)) { return vm->lib.nil; } return sl_make_int(vm, methp->arity); }
static SLVAL sl_method_arity(sl_vm_t* vm, SLVAL method) { sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); if(!methp->initialized) { return vm->lib.nil; } return sl_make_int(vm, methp->arity); }
static SLVAL sl_new_oserror(sl_vm_t* vm, int errno) { char* description = strerror(errno); SLVAL Posix_OSError = vm->store[cPosix_OSError]; SLVAL error = sl_make_error2(vm, Posix_OSError, sl_make_cstring(vm, description)); sl_set_ivar(vm, error, sl_intern(vm, "errno"), sl_make_int(vm, errno)); return error; }
SLVAL sl_float_cmp(sl_vm_t* vm, SLVAL self, SLVAL other) { if(sl_is_a(vm, other, vm->lib.Int)) { return sl_float_cmp(vm, self, sl_make_float(vm, sl_get_int(other))); } if(sl_is_a(vm, other, vm->lib.Bignum)) { return sl_make_int(vm, -sl_get_int(sl_bignum_cmp(vm, other, self))); } sl_expect(vm, other, vm->lib.Float); if(sl_get_float(vm, self) < sl_get_float(vm, other)) { return sl_make_int(vm, -1); } else if(sl_get_float(vm, self) > sl_get_float(vm, other)) { return sl_make_int(vm, 1); } else { return sl_make_int(vm, 0); } }
static SLVAL enumerable_length(sl_vm_t* vm, SLVAL self) { SLVAL enumerator = sl_send(vm, self, "enumerate", 0); int i = 0; while(sl_is_truthy(sl_send(vm, enumerator, "next", 0))) { i++; } return sl_make_int(vm, i); }
static SLVAL sl_float_to_i(sl_vm_t* vm, SLVAL self) { double d = sl_get_float(vm, self); if(d > INT_MAX / 2 || d < INT_MIN / 2) { return sl_make_bignum_f(vm, d); } else { return sl_make_int(vm, (int)d); } }
static SLVAL sl_float_round(sl_vm_t* vm, SLVAL self) { double d = round(sl_get_float(vm, self)); if(d < SL_MIN_INT || d > SL_MAX_INT) { return sl_make_bignum_f(vm, d); } else { return sl_make_int(vm, (int)d); } }
SLVAL sl_regexp_match_byte_offset(sl_vm_t* vm, SLVAL self, SLVAL i) { sl_regexp_match_t* match = get_regexp_match(vm, self); int index = cap_index(vm, self, i); if(index < 0) { return vm->lib.nil; } return sl_make_int(vm, match->captures[index]); }
SLVAL sl_regexp_match_offset(sl_vm_t* vm, SLVAL self, SLVAL i) { sl_regexp_match_t* match = get_regexp_match(vm, self); int index = cap_index(vm, self, i); if(index < 0) { return vm->lib.nil; } int offset = match->captures[index]; return sl_make_int(vm, sl_string_index_for_byte_offset(vm, match->match_string, offset)); }
static int on_integer(void* ctx, long long val) { json_parse_t* json = ctx; char buff[256]; SLVAL i; if(val >= SL_MAX_INT || val <= SL_MIN_INT) { sprintf(buff, "%lld", val); i = sl_make_bignum_s(json->vm, buff); } else { i = sl_make_int(json->vm, (int)val); } JSON_ADD_VALUE(i); return 1; }
SLVAL sl_string_index(sl_vm_t* vm, SLVAL self, SLVAL substr) { sl_string_t* haystack = sl_get_string(vm, self); sl_string_t* needle = sl_get_string(vm, substr); /* @TODO use a more efficient algorithm */ uint8_t* haystack_buff = haystack->buff; size_t haystack_len = haystack->buff_len; size_t i = 0; while(haystack_len >= needle->buff_len) { if(memcmp(haystack_buff, needle->buff, needle->buff_len) == 0) { return sl_make_int(vm, i); } sl_utf8_each_char(vm, &haystack_buff, &haystack_len); i++; } return vm->lib.nil; }
SLVAL sl_integer_parse(sl_vm_t* vm, uint8_t* str, size_t len) { mpz_t mpz; char* buff = sl_alloc(vm->arena, len + 1); memcpy(buff, str, len); buff[len] = 0; char* dec = memchr(buff, '.', len); if(dec) { *dec = 0; } mpz_init_set_str(mpz, buff, 10); if(mpz_cmp_si(mpz, INT_MIN / 2) > 0 && mpz_cmp_si(mpz, INT_MAX / 2) < 0) { SLVAL retn = sl_make_int(vm, mpz_get_si(mpz)); mpz_clear(mpz); return retn; } else { mpz_clear(mpz); return sl_make_bignum_s(vm, buff); } }
static SLVAL sl_mysql_raw_query(sl_vm_t* vm, SLVAL self, SLVAL query) { mysql_t* mysql = get_mysql(vm, self); sl_string_t* str = sl_get_string(vm, query); if(mysql_real_query(&mysql->mysql, (char*)str->buff, str->buff_len)) { sl_mysql_check_error(vm, &mysql->mysql); } MYSQL_RES* result; if((result = mysql_store_result(&mysql->mysql))) { /* do shit */ int ncolumns = mysql_num_fields(result); int nrows = mysql_num_rows(result); SLVAL* rows = sl_alloc(vm->arena, sizeof(SLVAL) * nrows); MYSQL_FIELD* fields = mysql_fetch_fields(result); for(int i = 0; i < nrows; i++) { SLVAL* cells = sl_alloc(vm->arena, sizeof(SLVAL) * ncolumns * 2); MYSQL_ROW row = mysql_fetch_row(result); size_t* lengths = mysql_fetch_lengths(result); for(int j = 0; j < ncolumns; j++) { cells[j * 2] = sl_make_cstring(vm, fields[j].name); if(row[j]) { cells[j * 2 + 1] = sl_make_string(vm, (uint8_t*)row[j], lengths[j]); } else { cells[j * 2 + 1] = vm->lib.nil; } } rows[i] = sl_make_dict(vm, ncolumns, cells); } mysql_free_result(result); return sl_make_array(vm, nrows, rows); } else { if(mysql_field_count(&mysql->mysql) != 0) { sl_mysql_check_error(vm, &mysql->mysql); } return sl_make_int(vm, mysql_affected_rows(&mysql->mysql)); } }
static void emit_assignment(sl_compile_state_t* cs, sl_node_base_t* lval, size_t reg) { sl_node_assign_var_t a_var; sl_node_send_t send; size_t dest_reg = reg_alloc(cs); sl_node__register_t node; node.base.type = SL_NODE__REGISTER; node.base.line = 0; node.reg = reg; switch(lval->type) { case SL_NODE_VAR: a_var.base.type = SL_NODE_ASSIGN_VAR; break; case SL_NODE_IVAR: a_var.base.type = SL_NODE_ASSIGN_IVAR; break; case SL_NODE_CVAR: a_var.base.type = SL_NODE_ASSIGN_CVAR; break; case SL_NODE_CONST: a_var.base.type = SL_NODE_ASSIGN_CONST; break; case SL_NODE_ARRAY: a_var.base.type = SL_NODE_ASSIGN_ARRAY; break; case SL_NODE_SEND: /* special case that turns a.b = 1 into a.send("b=", 1) */ /* this is separate to the other method of handling send assignments which also handles compound assignments. */ memcpy(&send, lval, sizeof(sl_node_send_t)); send.id = sl_id_make_setter(cs->vm, send.id); sl_node_base_t** args = sl_alloc(cs->vm->arena, sizeof(sl_node_base_t*) * (send.arg_count + 1)); memcpy(args, send.args, sizeof(sl_node_base_t*) * send.arg_count); args[send.arg_count++] = (sl_node_base_t*)&node; send.args = args; compile_node(cs, (sl_node_base_t*)&send, dest_reg); reg_free(cs, dest_reg); return; default: { SLVAL err = sl_make_cstring(cs->vm, "Invalid lval in assignment"); err = sl_make_error2(cs->vm, cs->vm->lib.SyntaxError, err); sl_error_add_frame(cs->vm, err, sl_make_cstring(cs->vm, "<compiler>"), sl_make_cstring(cs->vm, (char*)cs->section->filename), sl_make_int(cs->vm, lval->line)); sl_throw(cs->vm, err); } } a_var.base.line = 0; a_var.lval = (void*)lval; a_var.rval = (sl_node_base_t*)&node; compile_node(cs, (sl_node_base_t*)&a_var, dest_reg); reg_free(cs, dest_reg); }
NODE(sl_node_var_t, var) { size_t frame; sl_compile_state_t* xcs = cs; size_t index = 0xCAFE; SLVAL err; frame = 0; while(xcs) { if(sl_st_lookup(xcs->vars, (sl_st_data_t)node->name, (sl_st_data_t*)&index)) { if(frame == 0) { op_mov(cs, index, dest); } else { op_get_outer(cs, frame, index, dest); mark_upper_scopes_as_closure_unsafe(cs, frame); } return; } xcs = xcs->parent; frame++; } err = sl_make_formatted_string(cs->vm, "Undefined variable %QV", node->name); err = sl_make_error2(cs->vm, cs->vm->lib.NameError, err); sl_error_add_frame(cs->vm, err, sl_make_cstring(cs->vm, "<compiler>"), sl_make_cstring(cs->vm, (char*)cs->section->filename), sl_make_int(cs->vm, node->base.line)); sl_throw(cs->vm, err); }
static SLVAL response_status(sl_vm_t* vm) { return sl_make_int(vm, response(vm)->status); }
static SLVAL sl_mysql_stmt_execute(sl_vm_t* vm, SLVAL self, size_t argc, SLVAL* argv) { mysql_stmt_t* stmt = get_mysql_stmt(vm, self); size_t req = mysql_stmt_param_count(stmt->stmt); if(argc < req) { char buff[100]; sprintf(buff, "Prepared statement has %lu parameter markers, but only %lu parameters were given", req, argc); sl_throw_message2(vm, vm->lib.ArgumentError, buff); } if(!stmt->bind) { stmt->bind = sl_alloc(vm->arena, sizeof(MYSQL_BIND) * req); } for(size_t i = 0; i < req; i++) { stmt->bind[i].buffer_type = MYSQL_TYPE_STRING; sl_string_t* str = sl_get_string(vm, sl_to_s(vm, argv[i])); stmt->bind[i].buffer = str->buff; stmt->bind[i].buffer_length = str->buff_len; stmt->bind[i].length = NULL; stmt->bind[i].is_null = NULL; stmt->bind[i].is_unsigned = 1; stmt->bind[i].error = NULL; } if(mysql_stmt_bind_param(stmt->stmt, stmt->bind)) { sl_mysql_stmt_check_error(vm, stmt->stmt); } if(mysql_stmt_execute(stmt->stmt)) { sl_mysql_stmt_check_error(vm, stmt->stmt); } MYSQL_RES* res = mysql_stmt_result_metadata(stmt->stmt); if(!res) { /* query did not produce a result set */ return sl_make_int(vm, mysql_stmt_affected_rows(stmt->stmt)); } int field_count = mysql_stmt_field_count(stmt->stmt); MYSQL_FIELD* field; SLVAL field_names[field_count]; enum enum_field_types field_types[field_count]; size_t field_i = 0; while((field = mysql_fetch_field(res))) { field_names[field_i] = sl_make_cstring(vm, field->name); if(field->type == MYSQL_TYPE_LONG || field->type == MYSQL_TYPE_SHORT || field->type == MYSQL_TYPE_TINY) { field_types[field_i] = MYSQL_TYPE_LONG; } else { field_types[field_i] = MYSQL_TYPE_STRING; } field_i++; } MYSQL_BIND output_binds[field_count]; my_bool output_errors[field_count]; my_bool output_is_nulls[field_count]; unsigned long output_lengths[field_count]; for(int i = 0; i < field_count; i++) { output_binds[i].buffer_type = MYSQL_TYPE_STRING; output_binds[i].buffer = NULL; output_binds[i].buffer_length = 0; output_binds[i].length = &output_lengths[i]; output_binds[i].is_null = &output_is_nulls[i]; output_binds[i].error = &output_errors[i]; } if(mysql_stmt_bind_result(stmt->stmt, output_binds)) { sl_mysql_stmt_check_error(vm, stmt->stmt); } SLVAL result_rows = sl_make_array(vm, 0, NULL); while(1) { int code = mysql_stmt_fetch(stmt->stmt); if(code == MYSQL_NO_DATA) { break; } if(code == 1) { sl_mysql_stmt_check_error(vm, stmt->stmt); } SLVAL row = sl_make_dict(vm, 0, NULL); for(int i = 0; i < field_count; i++) { MYSQL_BIND cell; cell.length = &output_lengths[i]; cell.is_null = &output_is_nulls[i]; cell.error = &output_errors[i]; cell.buffer_type = field_types[i]; int buffer_long; switch(field_types[i]) { case MYSQL_TYPE_LONG: cell.buffer = &buffer_long; cell.buffer_length = sizeof(buffer_long); break; default: /* MYSQL_TYPE_STRING */ cell.buffer = sl_alloc_buffer(vm->arena, output_lengths[i] + 1); cell.buffer_length = output_lengths[i]; break; } if(mysql_stmt_fetch_column(stmt->stmt, &cell, i, 0)) { sl_mysql_stmt_check_error(vm, stmt->stmt); } switch(field_types[i]) { case MYSQL_TYPE_LONG: sl_dict_set(vm, row, field_names[i], sl_make_int(vm, buffer_long)); break; default: /* MYSQL_TYPE_STRING */ sl_dict_set(vm, row, field_names[i], sl_make_string(vm, cell.buffer, output_lengths[i])); break; } } sl_array_push(vm, result_rows, 1, &row); } return result_rows; }
static SLVAL sl_regexp_options(sl_vm_t* vm, SLVAL self) { return sl_make_int(vm, get_regexp_check(vm, self)->options & ~DEFAULT_OPTIONS); }
void sl_parse_error(sl_parse_state_t* ps, char* message) { SLVAL err = sl_make_error2(ps->vm, ps->vm->lib.SyntaxError, sl_make_cstring(ps->vm, message)); sl_error_add_frame(ps->vm, err, sl_make_cstring(ps->vm, "<parser>"), sl_make_cstring(ps->vm, (char*)ps->filename), sl_make_int(ps->vm, ps->line)); sl_throw(ps->vm, err); }
static sl_node_base_t* primary_expression(sl_parse_state_t* ps) { sl_token_t* tok; sl_node_base_t* node; switch(peek_token(ps)->type) { case SL_TOK_INTEGER: tok = next_token(ps); return sl_make_immediate_node(ps, sl_integer_parse(ps->vm, tok->as.str.buff, tok->as.str.len)); case SL_TOK_FLOAT: return sl_make_immediate_node(ps, sl_make_float(ps->vm, next_token(ps)->as.dbl)); case SL_TOK_STRING: return string_expression(ps); case SL_TOK_REGEXP: return regexp_expression(ps); case SL_TOK_CONSTANT: tok = next_token(ps); return sl_make_const_node(ps, NULL, sl_intern2(ps->vm, sl_make_string(ps->vm, tok->as.str.buff, tok->as.str.len))); case SL_TOK_IDENTIFIER: tok = next_token(ps); return sl_make_var_node(ps, SL_NODE_VAR, sl_make_string(ps->vm, tok->as.str.buff, tok->as.str.len)); case SL_TOK_TRUE: next_token(ps); return sl_make_immediate_node(ps, ps->vm->lib._true); case SL_TOK_FALSE: next_token(ps); return sl_make_immediate_node(ps, ps->vm->lib._false); case SL_TOK_NIL: next_token(ps); return sl_make_immediate_node(ps, ps->vm->lib.nil); case SL_TOK_SELF: next_token(ps); return sl_make_self_node(ps); case SL_TOK_IVAR: tok = next_token(ps); node = sl_make_var_node(ps, SL_NODE_IVAR, sl_make_string(ps->vm, tok->as.str.buff, tok->as.str.len)); return node; case SL_TOK_CVAR: tok = next_token(ps); node = sl_make_var_node(ps, SL_NODE_CVAR, sl_make_string(ps->vm, tok->as.str.buff, tok->as.str.len)); return node; case SL_TOK_IF: case SL_TOK_UNLESS: return if_expression(ps); case SL_TOK_SWITCH: return switch_expression(ps); case SL_TOK_WHILE: case SL_TOK_UNTIL: return while_expression(ps); case SL_TOK_FOR: return for_expression(ps); case SL_TOK_CLASS: return class_expression(ps); case SL_TOK_DEF: return def_expression(ps); case SL_TOK_LAMBDA: return lambda_expression(ps); case SL_TOK_TRY: return try_expression(ps); case SL_TOK_OPEN_BRACKET: return array_expression(ps); case SL_TOK_OPEN_PAREN: return bracketed_expression(ps); case SL_TOK_OPEN_BRACE: return dict_expression(ps); case SL_TOK_NEXT: tok = next_token(ps); if(!(ps->scope->flags & SL_PF_CAN_NEXT_LAST)) { error(ps, sl_make_cstring(ps->vm, "next invalid outside loop"), tok); } return sl_make_singleton_node(ps, SL_NODE_NEXT); case SL_TOK_LAST: tok = next_token(ps); if(!(ps->scope->flags & SL_PF_CAN_NEXT_LAST)) { error(ps, sl_make_cstring(ps->vm, "last invalid outside loop"), tok); } return sl_make_singleton_node(ps, SL_NODE_LAST); case SL_TOK_SPECIAL_FILE: next_token(ps); return sl_make_immediate_node(ps, sl_make_cstring(ps->vm, (char*)ps->filename)); case SL_TOK_SPECIAL_LINE: return sl_make_immediate_node(ps, sl_make_int(ps->vm, next_token(ps)->line)); case SL_TOK_RANGE_EX: next_token(ps); return sl_make_singleton_node(ps, SL_NODE_YADA_YADA); default: unexpected(ps, peek_token(ps)); return NULL; } }
static SLVAL sl_mysql_stmt_param_count(sl_vm_t* vm, SLVAL self) { return sl_make_int(vm, mysql_stmt_param_count(get_mysql_stmt(vm, self)->stmt)); }
static void error(sl_parse_state_t* ps, SLVAL err, sl_token_t* tok) { err = sl_make_error2(ps->vm, ps->vm->lib.SyntaxError, err); sl_error_add_frame(ps->vm, err, sl_make_cstring(ps->vm, "<parser>"), sl_make_cstring(ps->vm, (char*)ps->filename), sl_make_int(ps->vm, tok->line)); sl_throw(ps->vm, err); }
static SLVAL sl_mysql_stmt_insert_id(sl_vm_t* vm, SLVAL self) { return sl_make_int(vm, mysql_stmt_insert_id(get_mysql_stmt(vm, self)->stmt)); }
void sl_lex_error(sl_lex_state_t* st, char* text, int lineno) { SLVAL msg = sl_make_formatted_string(st->vm, "Unexpected character '%s'", text); SLVAL err = sl_make_error2(st->vm, st->vm->lib.SyntaxError, msg); sl_error_add_frame(st->vm, err, sl_make_cstring(st->vm, "<lexer>"), sl_make_cstring(st->vm, (char*)st->filename), sl_make_int(st->vm, lineno)); sl_throw(st->vm, err); }
static SLVAL sl_mysql_insert_id(sl_vm_t* vm, SLVAL self) { mysql_t* mysql = get_mysql(vm, self); return sl_make_int(vm, mysql_insert_id(&mysql->mysql)); }
static SLVAL sl_regexp_match_length(sl_vm_t* vm, SLVAL self) { return sl_make_int(vm, get_regexp_match(vm, self)->capture_count); }