int read(int fd, void *buf, unsigned int bytes) { if (!_valid_file(fd)) { klogf("read: Invalid file %x\n", fd); return -1; } FSFile *file = (FSFile*) fd; _lock_file(file); int reverse = (!!(file->mode & O_PIPE_MODE)) ^ (!!(file->mode & _O_REVERSE)); if (!(file->access & O_RDONLY) || !(file->fs->accessmode(file->fs, file->device) & O_RDONLY)) { klogf("Permission error: %d\n", file->access); _set_error(FILE_PERMISSIONS_ERROR, "Invalid permissions"); _unlock_file(file); return -1; } if (!file->fs->pread) { klogf("Read not supported by filesystem: %x\n", file->fs->pread); _set_error(FILE_TYPE_ERROR, "Read not supported by the filesystem driver"); _unlock_file(file); return -1; } int ret, *cursor = ((file->mode & O_PIPE_MODE) && reverse) ? &file->pipe_rcursor : &file->cursor; ret = file->fs->pread(file->fs, file->device, file->internalfd, buf, bytes, *cursor); if (ret < 0) { _unlock_file(file); file->fs->lasterror_message[sizeof(file->fs->lasterror_message)-1] = 0; strncpy(file->errmsg, file->fs->lasterror_message, sizeof(file->errmsg)-1); file->errno = file->fs->lasterror; klogf("Read error:%d: %s\n", file->fs->lasterror, file->fs->lasterror_message); return -1; } *cursor += ret; //klogf("cursorr: %d, ret: %d\n", *cursor, ret); if (file->mode & O_PIPE_MODE) { struct stat sdata; file->fs->stat(file->fs, file->device, (int)file, &sdata); unsigned int size = sdata.st_size; *cursor = *cursor % size; } _unlock_file(file); return ret; }
int pread(int fd, void *buf, unsigned int bytes, unsigned int off) { if (!_valid_file(fd)) { klogf("pread: Invalid file %x\n", fd); return -1; } FSFile *file = (FSFile*) fd; _lock_file(file); //int reverse = (!!(file->mode & O_PIPE_MODE)) ^ (!!(file->mode & _O_REVERSE)); if (!(file->access & O_RDONLY) || !(file->fs->accessmode(file->fs, file->device) & O_RDONLY)) { klogf("Permission error: %d\n", file->access); _set_error(FILE_PERMISSIONS_ERROR, "Invalid permissions"); _unlock_file(file); return -1; } if (!file->fs->pread) { klogf("Read not supported by filesystem: %x\n", file->fs->pread); _set_error(FILE_TYPE_ERROR, "Read not supported by the filesystem driver"); _unlock_file(file); return -1; } int ret = file->fs->pread(file->fs, file->device, file->internalfd, buf, bytes, off); //wrap around in pipe mode if (ret > 0 && ret < (int)bytes && (file->mode & O_PIPE_MODE)) { ret = file->fs->pread(file->fs, file->device, file->internalfd, buf+ret, bytes-ret, 0); } if (ret < 0) { _unlock_file(file); file->fs->lasterror_message[sizeof(file->fs->lasterror_message)-1] = 0; strncpy(file->errmsg, file->fs->lasterror_message, sizeof(file->errmsg)-1); file->errno = file->fs->lasterror; klogf("Read error:%d: %s\n", file->fs->lasterror, file->fs->lasterror_message); return -1; } //klogf("cursorr: %d, ret: %d\n", *cursor, ret); _unlock_file(file); return ret; }
int write(int fd, void *buf, unsigned int bytes) { if (!_valid_file(fd)) return -1; FSFile *file = (FSFile*) fd; _lock_file(file); int reverse = (!!(file->mode & O_PIPE_MODE)) ^ (!!(file->mode & _O_REVERSE)); if (!(file->access & O_RDONLY) || !(file->fs->accessmode(file->fs, file->device) & O_RDONLY)) { _set_error(FILE_PERMISSIONS_ERROR, "Invalid permissions"); _unlock_file(file); return -1; } if (!file->fs->pread) { _set_error(FILE_TYPE_ERROR, "Read not supported by the filesystem driver"); _unlock_file(file); return -1; } int ret, *cursor = !((file->mode & O_PIPE_MODE) && reverse) ? &file->pipe_rcursor : &file->cursor; ret = file->fs->pwrite(file->fs, file->device, file->internalfd, buf, bytes, *cursor); if (!(file->mode & O_NOSEEK)) { *cursor += ret; } //kprintf("cursorw: %d, ret: %d\n", *cursor, ret); if (file->mode & O_PIPE_MODE) { struct stat sdata; file->fs->stat(file->fs, file->device, (int)file, &sdata); unsigned int size = sdata.st_size; if (*cursor != (int)size) { *cursor = *cursor % size; } } _unlock_file(file); return ret; }
VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { Vector<Expression> expression; while (true) { //keep appending stuff to expression ENode *expr = NULL; Token tk; _get_token(tk); if (error_set) return NULL; switch (tk.type) { case TK_CURLY_BRACKET_OPEN: { //a dictionary DictionaryNode *dn = alloc_node<DictionaryNode>(); while (true) { int cofs = str_ofs; _get_token(tk); if (tk.type == TK_CURLY_BRACKET_CLOSE) { break; } str_ofs = cofs; //revert //parse an expression ENode *expr = _parse_expression(); if (!expr) return NULL; dn->dict.push_back(expr); _get_token(tk); if (tk.type != TK_COLON) { _set_error("Expected ':'"); return NULL; } expr = _parse_expression(); if (!expr) return NULL; dn->dict.push_back(expr); cofs = str_ofs; _get_token(tk); if (tk.type == TK_COMMA) { //all good } else if (tk.type == TK_CURLY_BRACKET_CLOSE) { str_ofs = cofs; } else { _set_error("Expected ',' or '}'"); } } expr = dn; } break; case TK_BRACKET_OPEN: { //an array ArrayNode *an = alloc_node<ArrayNode>(); while (true) { int cofs = str_ofs; _get_token(tk); if (tk.type == TK_BRACKET_CLOSE) { break; } str_ofs = cofs; //revert //parse an expression ENode *expr = _parse_expression(); if (!expr) return NULL; an->array.push_back(expr); cofs = str_ofs; _get_token(tk); if (tk.type == TK_COMMA) { //all good } else if (tk.type == TK_BRACKET_CLOSE) { str_ofs = cofs; } else { _set_error("Expected ',' or ']'"); } } expr = an; } break; case TK_PARENTHESIS_OPEN: { //a suexpression ENode *e = _parse_expression(); if (error_set) return NULL; _get_token(tk); if (tk.type != TK_PARENTHESIS_CLOSE) { _set_error("Expected ')'"); return NULL; } expr = e; } break; case TK_IDENTIFIER: { String what = tk.value; int index = -1; for (int i = 0; i < inputs.size(); i++) { if (what == inputs[i].name) { index = i; break; } } if (index != -1) { InputNode *input = alloc_node<InputNode>(); input->index = index; expr = input; } else { _set_error("Invalid input identifier '" + what + "'. For script variables, use self (locals are for inputs)." + what); return NULL; } } break; case TK_SELF: { SelfNode *self = alloc_node<SelfNode>(); expr = self; } break; case TK_CONSTANT: { ConstantNode *constant = alloc_node<ConstantNode>(); constant->value = tk.value; expr = constant; } break; case TK_BASIC_TYPE: { //constructor.. Variant::Type bt = Variant::Type(int(tk.value)); _get_token(tk); if (tk.type != TK_PARENTHESIS_OPEN) { _set_error("Expected '('"); return NULL; } ConstructorNode *constructor = alloc_node<ConstructorNode>(); constructor->data_type = bt; while (true) { int cofs = str_ofs; _get_token(tk); if (tk.type == TK_PARENTHESIS_CLOSE) { break; } str_ofs = cofs; //revert //parse an expression ENode *expr = _parse_expression(); if (!expr) return NULL; constructor->arguments.push_back(expr); cofs = str_ofs; _get_token(tk); if (tk.type == TK_COMMA) { //all good } else if (tk.type == TK_PARENTHESIS_CLOSE) { str_ofs = cofs; } else { _set_error("Expected ',' or ')'"); } } expr = constructor; } break; case TK_BUILTIN_FUNC: { //builtin function _get_token(tk); if (tk.type != TK_PARENTHESIS_OPEN) { _set_error("Expected '('"); return NULL; } BuiltinFuncNode *bifunc = alloc_node<BuiltinFuncNode>(); bifunc->func = VisualScriptBuiltinFunc::BuiltinFunc(int(tk.value)); while (true) { int cofs = str_ofs; _get_token(tk); if (tk.type == TK_PARENTHESIS_CLOSE) { break; } str_ofs = cofs; //revert //parse an expression ENode *expr = _parse_expression(); if (!expr) return NULL; bifunc->arguments.push_back(expr); cofs = str_ofs; _get_token(tk); if (tk.type == TK_COMMA) { //all good } else if (tk.type == TK_PARENTHESIS_CLOSE) { str_ofs = cofs; } else { _set_error("Expected ',' or ')'"); } } int expected_args = VisualScriptBuiltinFunc::get_func_argument_count(bifunc->func); if (bifunc->arguments.size() != expected_args) { _set_error("Builtin func '" + VisualScriptBuiltinFunc::get_func_name(bifunc->func) + "' expects " + itos(expected_args) + " arguments."); } expr = bifunc; } break; case TK_OP_SUB: { Expression e; e.is_op = true; e.op = Variant::OP_NEGATE; expression.push_back(e); continue; } break; case TK_OP_NOT: { Expression e; e.is_op = true; e.op = Variant::OP_NOT; expression.push_back(e); continue; } break; default: { _set_error("Expected expression."); return NULL; } break; } //before going to operators, must check indexing! while (true) { int cofs2 = str_ofs; _get_token(tk); if (error_set) return NULL; bool done = false; switch (tk.type) { case TK_BRACKET_OPEN: { //value indexing IndexNode *index = alloc_node<IndexNode>(); index->base = expr; ENode *what = _parse_expression(); if (!what) return NULL; index->index = what; _get_token(tk); if (tk.type != TK_BRACKET_CLOSE) { _set_error("Expected ']' at end of index."); return NULL; } expr = index; } break; case TK_PERIOD: { //named indexing or function call _get_token(tk); if (tk.type != TK_IDENTIFIER) { _set_error("Expected identifier after '.'"); return NULL; } StringName identifier = tk.value; int cofs = str_ofs; _get_token(tk); if (tk.type == TK_PARENTHESIS_OPEN) { //function call CallNode *func_call = alloc_node<CallNode>(); func_call->method = identifier; func_call->base = expr; while (true) { int cofs = str_ofs; _get_token(tk); if (tk.type == TK_PARENTHESIS_CLOSE) { break; } str_ofs = cofs; //revert //parse an expression ENode *expr = _parse_expression(); if (!expr) return NULL; func_call->arguments.push_back(expr); cofs = str_ofs; _get_token(tk); if (tk.type == TK_COMMA) { //all good } else if (tk.type == TK_PARENTHESIS_CLOSE) { str_ofs = cofs; } else { _set_error("Expected ',' or ')'"); } } expr = func_call; } else { //named indexing str_ofs = cofs; NamedIndexNode *index = alloc_node<NamedIndexNode>(); index->base = expr; index->name = identifier; expr = index; } } break; default: { str_ofs = cofs2; done = true; } break; } if (done) break; } //push expression { Expression e; e.is_op = false; e.node = expr; expression.push_back(e); } //ok finally look for an operator int cofs = str_ofs; _get_token(tk); if (error_set) return NULL; Variant::Operator op = Variant::OP_MAX; switch (tk.type) { case TK_OP_IN: op = Variant::OP_IN; break; case TK_OP_EQUAL: op = Variant::OP_EQUAL; break; case TK_OP_NOT_EQUAL: op = Variant::OP_NOT_EQUAL; break; case TK_OP_LESS: op = Variant::OP_LESS; break; case TK_OP_LESS_EQUAL: op = Variant::OP_LESS_EQUAL; break; case TK_OP_GREATER: op = Variant::OP_GREATER; break; case TK_OP_GREATER_EQUAL: op = Variant::OP_GREATER_EQUAL; break; case TK_OP_AND: op = Variant::OP_AND; break; case TK_OP_OR: op = Variant::OP_OR; break; case TK_OP_NOT: op = Variant::OP_NOT; break; case TK_OP_ADD: op = Variant::OP_ADD; break; case TK_OP_SUB: op = Variant::OP_SUBTRACT; break; case TK_OP_MUL: op = Variant::OP_MULTIPLY; break; case TK_OP_DIV: op = Variant::OP_DIVIDE; break; case TK_OP_MOD: op = Variant::OP_MODULE; break; case TK_OP_SHIFT_LEFT: op = Variant::OP_SHIFT_LEFT; break; case TK_OP_SHIFT_RIGHT: op = Variant::OP_SHIFT_RIGHT; break; case TK_OP_BIT_AND: op = Variant::OP_BIT_AND; break; case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; default: {}; } if (op == Variant::OP_MAX) { //stop appending stuff str_ofs = cofs; break; } //push operator and go on { Expression e; e.is_op = true; e.op = op; expression.push_back(e); } } /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ while (expression.size() > 1) { int next_op = -1; int min_priority = 0xFFFFF; bool is_unary = false; for (int i = 0; i < expression.size(); i++) { if (!expression[i].is_op) { continue; } int priority; bool unary = false; switch (expression[i].op) { case Variant::OP_BIT_NEGATE: priority = 0; unary = true; break; case Variant::OP_NEGATE: priority = 1; unary = true; break; case Variant::OP_MULTIPLY: priority = 2; break; case Variant::OP_DIVIDE: priority = 2; break; case Variant::OP_MODULE: priority = 2; break; case Variant::OP_ADD: priority = 3; break; case Variant::OP_SUBTRACT: priority = 3; break; case Variant::OP_SHIFT_LEFT: priority = 4; break; case Variant::OP_SHIFT_RIGHT: priority = 4; break; case Variant::OP_BIT_AND: priority = 5; break; case Variant::OP_BIT_XOR: priority = 6; break; case Variant::OP_BIT_OR: priority = 7; break; case Variant::OP_LESS: priority = 8; break; case Variant::OP_LESS_EQUAL: priority = 8; break; case Variant::OP_GREATER: priority = 8; break; case Variant::OP_GREATER_EQUAL: priority = 8; break; case Variant::OP_EQUAL: priority = 8; break; case Variant::OP_NOT_EQUAL: priority = 8; break; case Variant::OP_IN: priority = 10; break; case Variant::OP_NOT: priority = 11; unary = true; break; case Variant::OP_AND: priority = 12; break; case Variant::OP_OR: priority = 13; break; default: { _set_error("Parser bug, invalid operator in expression: " + itos(expression[i].op)); return NULL; } } if (priority < min_priority) { // < is used for left to right (default) // <= is used for right to left next_op = i; min_priority = priority; is_unary = unary; } } if (next_op == -1) { _set_error("Yet another parser bug...."); ERR_FAIL_COND_V(next_op == -1, NULL); } // OK! create operator.. if (is_unary) { int expr_pos = next_op; while (expression[expr_pos].is_op) { expr_pos++; if (expr_pos == expression.size()) { //can happen.. _set_error("Unexpected end of expression.."); return NULL; } } //consecutively do unary opeators for (int i = expr_pos - 1; i >= next_op; i--) { OperatorNode *op = alloc_node<OperatorNode>(); op->op = expression[i].op; op->nodes[0] = expression[i + 1].node; op->nodes[1] = NULL; expression[i].is_op = false; expression[i].node = op; expression.remove(i + 1); } } else { if (next_op < 1 || next_op >= (expression.size() - 1)) { _set_error("Parser bug.."); ERR_FAIL_V(NULL); } OperatorNode *op = alloc_node<OperatorNode>(); op->op = expression[next_op].op; if (expression[next_op - 1].is_op) { _set_error("Parser bug.."); ERR_FAIL_V(NULL); } if (expression[next_op + 1].is_op) { // this is not invalid and can really appear // but it becomes invalid anyway because no binary op // can be followed by a unary op in a valid combination, // due to how precedence works, unaries will always disappear first _set_error("Unexpected two consecutive operators."); return NULL; } op->nodes[0] = expression[next_op - 1].node; //expression goes as left op->nodes[1] = expression[next_op + 1].node; //next expression goes as right //replace all 3 nodes by this operator and make it an expression expression[next_op - 1].node = op; expression.remove(next_op); expression.remove(next_op); } } return expression[0].node; }
Error VisualScriptExpression::_get_token(Token &r_token) { while (true) { #define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) CharType cchar = GET_CHAR(); if (cchar == 0) { r_token.type = TK_EOF; return OK; } switch (cchar) { case 0: { r_token.type = TK_EOF; return OK; } break; case '{': { r_token.type = TK_CURLY_BRACKET_OPEN; return OK; }; case '}': { r_token.type = TK_CURLY_BRACKET_CLOSE; return OK; }; case '[': { r_token.type = TK_BRACKET_OPEN; return OK; }; case ']': { r_token.type = TK_BRACKET_CLOSE; return OK; }; case '(': { r_token.type = TK_PARENTHESIS_OPEN; return OK; }; case ')': { r_token.type = TK_PARENTHESIS_CLOSE; return OK; }; case ',': { r_token.type = TK_COMMA; return OK; }; case ':': { r_token.type = TK_COLON; return OK; }; case '.': { r_token.type = TK_PERIOD; return OK; }; case '=': { cchar = GET_CHAR(); if (cchar == '=') { r_token.type = TK_OP_EQUAL; } else { _set_error("Expected '='"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } return OK; }; case '!': { if (expression[str_ofs] == '=') { r_token.type = TK_OP_NOT_EQUAL; str_ofs++; } else { r_token.type = TK_OP_NOT; } return OK; }; case '>': { if (expression[str_ofs] == '=') { r_token.type = TK_OP_GREATER_EQUAL; str_ofs++; } else if (expression[str_ofs] == '>') { r_token.type = TK_OP_SHIFT_RIGHT; str_ofs++; } else { r_token.type = TK_OP_GREATER; } return OK; }; case '<': { if (expression[str_ofs] == '=') { r_token.type = TK_OP_LESS_EQUAL; str_ofs++; } else if (expression[str_ofs] == '<') { r_token.type = TK_OP_SHIFT_LEFT; str_ofs++; } else { r_token.type = TK_OP_LESS; } return OK; }; case '+': { r_token.type = TK_OP_ADD; return OK; }; case '-': { r_token.type = TK_OP_SUB; return OK; }; case '/': { r_token.type = TK_OP_DIV; return OK; }; case '*': { r_token.type = TK_OP_MUL; return OK; }; case '%': { r_token.type = TK_OP_MOD; return OK; }; case '&': { if (expression[str_ofs] == '&') { r_token.type = TK_OP_AND; str_ofs++; } else { r_token.type = TK_OP_BIT_AND; } return OK; }; case '|': { if (expression[str_ofs] == '|') { r_token.type = TK_OP_OR; str_ofs++; } else { r_token.type = TK_OP_BIT_OR; } return OK; }; case '^': { r_token.type = TK_OP_BIT_XOR; return OK; }; case '~': { r_token.type = TK_OP_BIT_INVERT; return OK; }; case '"': { String str; while (true) { CharType ch = GET_CHAR(); if (ch == 0) { _set_error("Unterminated String"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } else if (ch == '"') { break; } else if (ch == '\\') { //escaped characters... CharType next = GET_CHAR(); if (next == 0) { _set_error("Unterminated String"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } CharType res = 0; switch (next) { case 'b': res = 8; break; case 't': res = 9; break; case 'n': res = 10; break; case 'f': res = 12; break; case 'r': res = 13; break; case 'u': { //hexnumbarh - oct is deprecated for (int j = 0; j < 4; j++) { CharType c = GET_CHAR(); if (c == 0) { _set_error("Unterminated String"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { _set_error("Malformed hex constant in string"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } CharType v; if (c >= '0' && c <= '9') { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; v += 10; } else if (c >= 'A' && c <= 'F') { v = c - 'A'; v += 10; } else { ERR_PRINT("BUG"); v = 0; } res <<= 4; res |= v; } } break; //case '\"': res='\"'; break; //case '\\': res='\\'; break; //case '/': res='/'; break; default: { res = next; //r_err_str="Invalid escape sequence"; //return ERR_PARSE_ERROR; } break; } str += res; } else { str += ch; } } r_token.type = TK_CONSTANT; r_token.value = str; return OK; } break; default: { if (cchar <= 32) { break; } if (cchar == '-' || (cchar >= '0' && cchar <= '9')) { //a number String num; #define READING_SIGN 0 #define READING_INT 1 #define READING_DEC 2 #define READING_EXP 3 #define READING_DONE 4 int reading = READING_INT; if (cchar == '-') { num += '-'; cchar = GET_CHAR(); } CharType c = cchar; bool exp_sign = false; bool exp_beg = false; bool is_float = false; while (true) { switch (reading) { case READING_INT: { if (c >= '0' && c <= '9') { //pass } else if (c == '.') { reading = READING_DEC; is_float = true; } else if (c == 'e') { reading = READING_EXP; } else { reading = READING_DONE; } } break; case READING_DEC: { if (c >= '0' && c <= '9') { } else if (c == 'e') { reading = READING_EXP; } else { reading = READING_DONE; } } break; case READING_EXP: { if (c >= '0' && c <= '9') { exp_beg = true; } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { if (c == '-') is_float = true; exp_sign = true; } else { reading = READING_DONE; } } break; } if (reading == READING_DONE) break; num += String::chr(c); c = GET_CHAR(); } str_ofs--; r_token.type = TK_CONSTANT; if (is_float) r_token.value = num.to_double(); else r_token.value = num.to_int(); return OK; } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { String id; bool first = true; while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) { id += String::chr(cchar); cchar = GET_CHAR(); first = false; } str_ofs--; //go back one if (id == "in") { r_token.type = TK_OP_IN; } else if (id == "null") { r_token.type = TK_CONSTANT; r_token.value = Variant(); } else if (id == "true") { r_token.type = TK_CONSTANT; r_token.value = true; } else if (id == "false") { r_token.type = TK_CONSTANT; r_token.value = false; } else if (id == "PI") { r_token.type = TK_CONSTANT; r_token.value = Math_PI; } else if (id == "TAU") { r_token.type = TK_CONSTANT; r_token.value = Math_TAU; } else if (id == "INF") { r_token.type = TK_CONSTANT; r_token.value = Math_INF; } else if (id == "NAN") { r_token.type = TK_CONSTANT; r_token.value = Math_NAN; } else if (id == "not") { r_token.type = TK_OP_NOT; } else if (id == "or") { r_token.type = TK_OP_OR; } else if (id == "and") { r_token.type = TK_OP_AND; } else if (id == "self") { r_token.type = TK_SELF; } else { for (int i = 0; i < Variant::VARIANT_MAX; i++) { if (id == Variant::get_type_name(Variant::Type(i))) { r_token.type = TK_BASIC_TYPE; r_token.value = i; return OK; } } VisualScriptBuiltinFunc::BuiltinFunc bifunc = VisualScriptBuiltinFunc::find_function(id); if (bifunc != VisualScriptBuiltinFunc::FUNC_MAX) { r_token.type = TK_BUILTIN_FUNC; r_token.value = bifunc; return OK; } r_token.type = TK_IDENTIFIER; r_token.value = id; } return OK; } else { _set_error("Unexpected character."); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } } } } r_token.type = TK_ERROR; return ERR_PARSE_ERROR; }
/** * gsdl_tokenizer_next: * @self: A valid %GSDLTokenizer. * @result: (out callee-allocates): A %GSDLToken to initialize and fill in. * @err: (out) (allow-none): Location to store any error, may be %NULL. * * Fetches the next token from the input. Depending on the source of input, may set an error in one * of the %GSDL_SYNTAX_ERROR, %G_IO_CHANNEL_ERROR, or %G_CONVERT_ERROR domains. * * Returns: Whether a token could be successfully read. */ bool gsdl_tokenizer_next(GSDLTokenizer *self, GSDLToken **result, GError **err) { gunichar c, nc; int line; int col; retry: line = self->line; col = self->col; if (!_read(self, &c, err)) return false; if (G_UNLIKELY(c == EOF)) { *result = _maketoken(T_EOF, line, col); return true; } else if (c == '\r') { if (_peek(self, &c, err) && c == '\n') _consume(self); *result = _maketoken('\n', line, col); FAIL_IF_ERR(); return true; } else if ((c == '/' && _peek(self, &nc, err) && nc == '/') || (c == '-' && _peek(self, &nc, err) && nc == '-') || c == '#') { if (c != '#') _consume(self); while (_peek(self, &c, err) && !(c == '\n' || c == EOF)) _consume(self); goto retry; } else if (c == '/' && _peek(self, &nc, err) && nc == '*') { while (_read(self, &c, err)) { if (c == EOF) { _set_error(err, self, GSDL_SYNTAX_ERROR_UNEXPECTED_CHAR, "Unterminated comment" ); return false; } else if (c == '*' && _peek(self, &c, err) && c == '/') { _consume(self); break; } } goto retry; } else if (c < 256 && strchr("-+:;./{}=\n", (char) c)) { *result = _maketoken(c, line, col); return true; } else if (c < 256 && isdigit((char) c)) { *result = _maketoken(T_NUMBER, line, col); return _tokenize_number(self, *result, c, err); } else if (g_unichar_isalpha(c) || g_unichar_type(c) == G_UNICODE_CONNECT_PUNCTUATION || g_unichar_type(c) == G_UNICODE_CURRENCY_SYMBOL) { *result = _maketoken(T_IDENTIFIER, line, col); return _tokenize_identifier(self, *result, c, err); } else if (c == '[') { *result = _maketoken(T_BINARY, line, col); if (!_tokenize_binary(self, *result, err)) return false; REQUIRE(_read(self, &c, err)); if (c == ']') { return true; } else { _set_error(err, self, GSDL_SYNTAX_ERROR_MISSING_DELIMITER, "Missing ']'" ); return false; } } else if (c == '"') { *result = _maketoken(T_STRING, line, col); if (!_tokenize_string(self, *result, err)) return false; REQUIRE(_read(self, &c, err)); if (c == '"') { return true; } else { _set_error(err, self, GSDL_SYNTAX_ERROR_MISSING_DELIMITER, "Missing '\"'" ); return false; } } else if (c == '`') { *result = _maketoken(T_STRING, line, col); if (!_tokenize_backquote_string(self, *result, err)) return false; REQUIRE(_read(self, &c, err)); if (c == '`') { return true; } else { _set_error(err, self, GSDL_SYNTAX_ERROR_MISSING_DELIMITER, "Missing '`'" ); return false; } } else if (c == '\'') { *result = _maketoken(T_CHAR, line, col); (*result)->val = g_malloc0(4); _read(self, &c, err); if (c == '\\') { _read(self, &c, err); switch (c) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case '"': c = '"'; break; case '\'': c = '\''; break; case '\\': c = '\\'; break; } } g_unichar_to_utf8(c, (*result)->val); REQUIRE(_read(self, &c, err)); if (c == '\'') { return true; } else { _set_error(err, self, GSDL_SYNTAX_ERROR_MISSING_DELIMITER, "Missing \"'\"" ); return false; } } else if (c == '\\' && _peek(self, &nc, err) && (nc == '\r' || nc == '\n')) { _consume(self); if (c == '\r') _read(self, &c, err); goto retry; } else if (c == ' ' || c == '\t') { // Do nothing goto retry; } else { _set_error(err, self, GSDL_SYNTAX_ERROR_UNEXPECTED_CHAR, g_strdup_printf("Invalid character '%s'(%d)", g_ucs4_to_utf8(&c, 1, NULL, NULL, NULL), c) ); return false; } }
//> Sub-tokenizers static bool _tokenize_number(GSDLTokenizer *self, GSDLToken *result, gunichar c, GError **err) { int length = 7; char *output = result->val = g_malloc(length); output[0] = c; int i = 1; while (_peek(self, &c, err) && c < 256 && isdigit(c)) { GROW_IF_NEEDED(output = result->val, i + 1, length); _consume(self); output[i++] = (gunichar) c; } FAIL_IF_ERR(); char *suffix = output + i; while (_peek(self, &c, err) && c < 256 && (isalpha(c) || isdigit(c))) { GROW_IF_NEEDED(output = result->val, i + 1, length); _consume(self); output[i++] = (gunichar) c; } FAIL_IF_ERR(); output[i] = '\0'; if (*suffix == '\0') { // Just a T_NUMBER if (c == ':') { _consume(self); result->type = T_TIME_PART; } else if (c == '/') { _consume(self); result->type = T_DATE_PART; } } else if (strcasecmp("bd", suffix) == 0) { result->type = T_DECIMAL_END; } else if (strcasecmp("d", suffix) == 0) { if (c == ':') { _consume(self); result->type = T_DAYS; } else { result->type = T_DOUBLE_END; } } else if (strcasecmp("f", suffix) == 0) { result->type = T_FLOAT_END; } else if (strcasecmp("l", suffix) == 0) { result->type = T_LONGINTEGER; } else { _set_error(err, self, GSDL_SYNTAX_ERROR_UNEXPECTED_CHAR, g_strdup_printf("Unexpected number suffix: \"%s\"", suffix)); return false; } *suffix = '\0'; return true; }
Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_block,int p_stack_level,int p_break_addr,int p_continue_addr) { codegen.push_stack_identifiers(); int new_identifiers=0; codegen.current_line=p_block->line; for(int i=0;i<p_block->statements.size();i++) { const GDParser::Node *s = p_block->statements[i]; switch(s->type) { case GDParser::Node::TYPE_NEWLINE: { const GDParser::NewLineNode *nl = static_cast<const GDParser::NewLineNode*>(s); codegen.opcodes.push_back(GDFunction::OPCODE_LINE); codegen.opcodes.push_back(nl->line); codegen.current_line=nl->line; } break; case GDParser::Node::TYPE_CONTROL_FLOW: { // try subblocks const GDParser::ControlFlowNode *cf = static_cast<const GDParser::ControlFlowNode*>(s); switch(cf->cf_type) { case GDParser::ControlFlowNode::CF_IF: { #ifdef DEBUG_ENABLED codegen.opcodes.push_back(GDFunction::OPCODE_LINE); codegen.opcodes.push_back(cf->line); codegen.current_line=cf->line; #endif int ret = _parse_expression(codegen,cf->arguments[0],p_stack_level,false); if (ret<0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT); codegen.opcodes.push_back(ret); int else_addr=codegen.opcodes.size(); codegen.opcodes.push_back(0); //temporary Error err = _parse_block(codegen,cf->body,p_stack_level,p_break_addr,p_continue_addr); if (err) return err; if (cf->body_else) { codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); int end_addr=codegen.opcodes.size(); codegen.opcodes.push_back(0); codegen.opcodes[else_addr]=codegen.opcodes.size(); Error err = _parse_block(codegen,cf->body_else,p_stack_level,p_break_addr,p_continue_addr); if (err) return err; codegen.opcodes[end_addr]=codegen.opcodes.size(); } else { //end without else codegen.opcodes[else_addr]=codegen.opcodes.size(); } } break; case GDParser::ControlFlowNode::CF_FOR: { int slevel=p_stack_level; int iter_stack_pos=slevel; int iterator_pos = (slevel++)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); int counter_pos = (slevel++)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); int container_pos = (slevel++)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); codegen.alloc_stack(slevel); codegen.push_stack_identifiers(); codegen.add_stack_identifier(static_cast<const GDParser::IdentifierNode*>(cf->arguments[0])->name,iter_stack_pos); int ret = _parse_expression(codegen,cf->arguments[1],slevel,false); if (ret<0) return ERR_COMPILATION_FAILED; //assign container codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN); codegen.opcodes.push_back(container_pos); codegen.opcodes.push_back(ret); //begin loop codegen.opcodes.push_back(GDFunction::OPCODE_ITERATE_BEGIN); codegen.opcodes.push_back(counter_pos); codegen.opcodes.push_back(container_pos); codegen.opcodes.push_back(codegen.opcodes.size()+4); codegen.opcodes.push_back(iterator_pos); codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); //skip code for next codegen.opcodes.push_back(codegen.opcodes.size()+8); //break loop int break_pos=codegen.opcodes.size(); codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); //skip code for next codegen.opcodes.push_back(0); //skip code for next //next loop int continue_pos=codegen.opcodes.size(); codegen.opcodes.push_back(GDFunction::OPCODE_ITERATE); codegen.opcodes.push_back(counter_pos); codegen.opcodes.push_back(container_pos); codegen.opcodes.push_back(break_pos); codegen.opcodes.push_back(iterator_pos); Error err = _parse_block(codegen,cf->body,slevel,break_pos,continue_pos); if (err) return err; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(continue_pos); codegen.opcodes[break_pos+1]=codegen.opcodes.size(); codegen.pop_stack_identifiers(); } break; case GDParser::ControlFlowNode::CF_WHILE: { codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(codegen.opcodes.size()+3); int break_addr=codegen.opcodes.size(); codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(0); int continue_addr=codegen.opcodes.size(); int ret = _parse_expression(codegen,cf->arguments[0],p_stack_level,false); if (ret<0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT); codegen.opcodes.push_back(ret); codegen.opcodes.push_back(break_addr); Error err = _parse_block(codegen,cf->body,p_stack_level,break_addr,continue_addr); if (err) return err; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(continue_addr); codegen.opcodes[break_addr+1]=codegen.opcodes.size(); } break; case GDParser::ControlFlowNode::CF_SWITCH: { } break; case GDParser::ControlFlowNode::CF_BREAK: { if (p_break_addr<0) { _set_error("'break'' not within loop",cf); return ERR_COMPILATION_FAILED; } codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(p_break_addr); } break; case GDParser::ControlFlowNode::CF_CONTINUE: { if (p_continue_addr<0) { _set_error("'continue' not within loop",cf); return ERR_COMPILATION_FAILED; } codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(p_continue_addr); } break; case GDParser::ControlFlowNode::CF_RETURN: { int ret; if (cf->arguments.size()) { ret = _parse_expression(codegen,cf->arguments[0],p_stack_level,false); if (ret<0) return ERR_PARSE_ERROR; } else { ret=GDFunction::ADDR_TYPE_NIL << GDFunction::ADDR_BITS; } codegen.opcodes.push_back(GDFunction::OPCODE_RETURN); codegen.opcodes.push_back(ret); } break; } } break; case GDParser::Node::TYPE_ASSERT: { // try subblocks const GDParser::AssertNode *as = static_cast<const GDParser::AssertNode*>(s); int ret = _parse_expression(codegen,as->condition,p_stack_level,false); if (ret<0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDFunction::OPCODE_ASSERT); codegen.opcodes.push_back(ret); } break; case GDParser::Node::TYPE_LOCAL_VAR: { const GDParser::LocalVarNode *lv = static_cast<const GDParser::LocalVarNode*>(s); codegen.add_stack_identifier(lv->name,p_stack_level++); codegen.alloc_stack(p_stack_level); new_identifiers++; } break; default: { //expression int ret = _parse_expression(codegen,s,p_stack_level,true); if (ret<0) return ERR_PARSE_ERROR; } break; } } codegen.pop_stack_identifiers(); return OK; }
int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expression, int p_stack_level,bool p_root) { switch(p_expression->type) { //should parse variable declaration and adjust stack accordingly... case GDParser::Node::TYPE_IDENTIFIER: { //return identifier //wait, identifier could be a local variable or something else... careful here, must reference properly //as stack may be more interesting to work with //This could be made much simpler by just indexing "self", but done this way (with custom self-addressing modes) increases peformance a lot. const GDParser::IdentifierNode *in = static_cast<const GDParser::IdentifierNode*>(p_expression); StringName identifier = in->name; // TRY STACK! if (codegen.stack_identifiers.has(identifier)) { int pos = codegen.stack_identifiers[identifier]; return pos|(GDFunction::ADDR_TYPE_STACK_VARIABLE<<GDFunction::ADDR_BITS); } //TRY ARGUMENTS! if (!codegen.function_node || !codegen.function_node->_static) { // TRY MEMBER VARIABLES! //static function if (codegen.script->member_indices.has(identifier)) { int idx = codegen.script->member_indices[identifier]; return idx|(GDFunction::ADDR_TYPE_MEMBER<<GDFunction::ADDR_BITS); //argument (stack root) } } //TRY CLASS CONSTANTS GDScript *owner = codegen.script; while (owner) { GDScript *scr = owner; GDNativeClass *nc=NULL; while(scr) { if (scr->constants.has(identifier)) { //int idx=scr->constants[identifier]; int idx = codegen.get_name_map_pos(identifier); return idx|(GDFunction::ADDR_TYPE_CLASS_CONSTANT<<GDFunction::ADDR_BITS); //argument (stack root) } if (scr->native.is_valid()) nc=scr->native.ptr(); scr=scr->_base; } // CLASS C++ Integer Constant if (nc) { bool success=false; int constant = ObjectTypeDB::get_integer_constant(nc->get_name(),identifier,&success); if (success) { Variant key=constant; int idx; if (!codegen.constant_map.has(key)) { idx=codegen.constant_map.size(); codegen.constant_map[key]=idx; } else { idx=codegen.constant_map[key]; } return idx|(GDFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDFunction::ADDR_BITS); //make it a local constant (faster access) } } owner=owner->_owner; } /* handled in constants now if (codegen.script->subclasses.has(identifier)) { //same with a subclass, make it a local constant. int idx = codegen.get_constant_pos(codegen.script->subclasses[identifier]); return idx|(GDFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDFunction::ADDR_BITS); //make it a local constant (faster access) }*/ if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) { int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; return idx|(GDFunction::ADDR_TYPE_GLOBAL<<GDFunction::ADDR_BITS); //argument (stack root) } //not found, error _set_error("Identifier not found: "+String(identifier),p_expression); return -1; } break; case GDParser::Node::TYPE_CONSTANT: { //return constant const GDParser::ConstantNode *cn = static_cast<const GDParser::ConstantNode*>(p_expression); int idx; if (!codegen.constant_map.has(cn->value)) { idx=codegen.constant_map.size(); codegen.constant_map[cn->value]=idx; } else { idx=codegen.constant_map[cn->value]; } return idx|(GDFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDFunction::ADDR_BITS); //argument (stack root) } break; case GDParser::Node::TYPE_SELF: { //return constant if (codegen.function_node && codegen.function_node->_static) { _set_error("'self' not present in static function!",p_expression); return -1; } return (GDFunction::ADDR_TYPE_SELF<<GDFunction::ADDR_BITS); } break; case GDParser::Node::TYPE_ARRAY: { const GDParser::ArrayNode *an = static_cast<const GDParser::ArrayNode*>(p_expression); Vector<int> values; int slevel=p_stack_level; for(int i=0;i<an->elements.size();i++) { int ret = _parse_expression(codegen,an->elements[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } values.push_back(ret); } codegen.opcodes.push_back(GDFunction::OPCODE_CONSTRUCT_ARRAY); codegen.opcodes.push_back(values.size()); for(int i=0;i<values.size();i++) codegen.opcodes.push_back(values[i]); int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode codegen.alloc_stack(p_stack_level); return dst_addr; } break; case GDParser::Node::TYPE_DICTIONARY: { const GDParser::DictionaryNode *dn = static_cast<const GDParser::DictionaryNode*>(p_expression); Vector<int> values; int slevel=p_stack_level; for(int i=0;i<dn->elements.size();i++) { int ret = _parse_expression(codegen,dn->elements[i].key,slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } values.push_back(ret); ret = _parse_expression(codegen,dn->elements[i].value,slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } values.push_back(ret); } codegen.opcodes.push_back(GDFunction::OPCODE_CONSTRUCT_DICTIONARY); codegen.opcodes.push_back(dn->elements.size()); for(int i=0;i<values.size();i++) codegen.opcodes.push_back(values[i]); int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode codegen.alloc_stack(p_stack_level); return dst_addr; } break; case GDParser::Node::TYPE_OPERATOR: { //hell breaks loose const GDParser::OperatorNode *on = static_cast<const GDParser::OperatorNode*>(p_expression); switch(on->op) { //call/constructor operator case GDParser::OperatorNode::OP_PARENT_CALL: { ERR_FAIL_COND_V(on->arguments.size()<1,-1); const GDParser::IdentifierNode *in = (const GDParser::IdentifierNode *)on->arguments[0]; Vector<int> arguments; int slevel = p_stack_level; for(int i=1;i<on->arguments.size();i++) { int ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } arguments.push_back(ret); } //push call bytecode codegen.opcodes.push_back(GDFunction::OPCODE_CALL_SELF_BASE); // basic type constructor codegen.opcodes.push_back(codegen.get_name_map_pos(in->name)); //instance codegen.opcodes.push_back(arguments.size()); //argument count codegen.alloc_call(arguments.size()); for(int i=0;i<arguments.size();i++) codegen.opcodes.push_back(arguments[i]); //arguments } break; case GDParser::OperatorNode::OP_CALL: { if (on->arguments[0]->type==GDParser::Node::TYPE_TYPE) { //construct a basic type ERR_FAIL_COND_V(on->arguments.size()<1,-1); const GDParser::TypeNode *tn = (const GDParser::TypeNode *)on->arguments[0]; int vtype = tn->vtype; Vector<int> arguments; int slevel = p_stack_level; for(int i=1;i<on->arguments.size();i++) { int ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } arguments.push_back(ret); } //push call bytecode codegen.opcodes.push_back(GDFunction::OPCODE_CONSTRUCT); // basic type constructor codegen.opcodes.push_back(vtype); //instance codegen.opcodes.push_back(arguments.size()); //argument count codegen.alloc_call(arguments.size()); for(int i=0;i<arguments.size();i++) codegen.opcodes.push_back(arguments[i]); //arguments } else if (on->arguments[0]->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) { //built in function ERR_FAIL_COND_V(on->arguments.size()<1,-1); Vector<int> arguments; int slevel = p_stack_level; for(int i=1;i<on->arguments.size();i++) { int ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } arguments.push_back(ret); } codegen.opcodes.push_back(GDFunction::OPCODE_CALL_BUILT_IN); codegen.opcodes.push_back(static_cast<const GDParser::BuiltInFunctionNode*>(on->arguments[0])->function); codegen.opcodes.push_back(on->arguments.size()-1); codegen.alloc_call(on->arguments.size()-1); for(int i=0;i<arguments.size();i++) codegen.opcodes.push_back(arguments[i]); } else { //regular function ERR_FAIL_COND_V(on->arguments.size()<2,-1); const GDParser::Node *instance = on->arguments[0]; bool in_static=false; if (instance->type==GDParser::Node::TYPE_SELF) { //room for optimization } Vector<int> arguments; int slevel = p_stack_level; for(int i=0;i<on->arguments.size();i++) { int ret; if (i==0 && on->arguments[i]->type==GDParser::Node::TYPE_SELF && codegen.function_node && codegen.function_node->_static) { //static call to self ret=(GDFunction::ADDR_TYPE_CLASS<<GDFunction::ADDR_BITS); } else if (i==1) { if (on->arguments[i]->type!=GDParser::Node::TYPE_IDENTIFIER) { _set_error("Attempt to call a non-identifier.",on); return -1; } GDParser::IdentifierNode *id = static_cast<GDParser::IdentifierNode*>(on->arguments[i]); ret=codegen.get_name_map_pos(id->name); } else { ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } } arguments.push_back(ret); } codegen.opcodes.push_back(p_root?GDFunction::OPCODE_CALL:GDFunction::OPCODE_CALL_RETURN); // perform operator codegen.opcodes.push_back(on->arguments.size()-2); codegen.alloc_call(on->arguments.size()-2); for(int i=0;i<arguments.size();i++) codegen.opcodes.push_back(arguments[i]); } } break; case GDParser::OperatorNode::OP_YIELD: { ERR_FAIL_COND_V(on->arguments.size() && on->arguments.size()!=2,-1); Vector<int> arguments; int slevel = p_stack_level; for(int i=0;i<on->arguments.size();i++) { int ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } arguments.push_back(ret); } //push call bytecode codegen.opcodes.push_back(arguments.size()==0?GDFunction::OPCODE_YIELD:GDFunction::OPCODE_YIELD_SIGNAL); // basic type constructor for(int i=0;i<arguments.size();i++) codegen.opcodes.push_back(arguments[i]); //arguments codegen.opcodes.push_back(GDFunction::OPCODE_YIELD_RESUME); //next will be where to place the result :) } break; //indexing operator case GDParser::OperatorNode::OP_INDEX: case GDParser::OperatorNode::OP_INDEX_NAMED: { ERR_FAIL_COND_V(on->arguments.size()!=2,-1); int slevel = p_stack_level; bool named=(on->op==GDParser::OperatorNode::OP_INDEX_NAMED); int from = _parse_expression(codegen,on->arguments[0],slevel); if (from<0) return from; int index; if (named) { index=codegen.get_name_map_pos(static_cast<GDParser::IdentifierNode*>(on->arguments[1])->name); } else { if (on->arguments[1]->type==GDParser::Node::TYPE_CONSTANT && static_cast<const GDParser::ConstantNode*>(on->arguments[1])->value.get_type()==Variant::STRING) { //also, somehow, named (speed up anyway) StringName name = static_cast<const GDParser::ConstantNode*>(on->arguments[1])->value; index=codegen.get_name_map_pos(name); named=true; } else { //regular indexing if (from&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } index = _parse_expression(codegen,on->arguments[1],slevel); if (index<0) return index; } } codegen.opcodes.push_back(named?GDFunction::OPCODE_GET_NAMED:GDFunction::OPCODE_GET); // perform operator codegen.opcodes.push_back(from); // argument 1 codegen.opcodes.push_back(index); // argument 2 (unary only takes one parameter) } break; case GDParser::OperatorNode::OP_AND: { // AND operator with early out on failure int res = _parse_expression(codegen,on->arguments[0],p_stack_level); if (res<0) return res; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT); codegen.opcodes.push_back(res); int jump_fail_pos=codegen.opcodes.size(); codegen.opcodes.push_back(0); res = _parse_expression(codegen,on->arguments[1],p_stack_level); if (res<0) return res; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT); codegen.opcodes.push_back(res); int jump_fail_pos2=codegen.opcodes.size(); codegen.opcodes.push_back(0); codegen.alloc_stack(p_stack_level); //it will be used.. codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN_TRUE); codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(codegen.opcodes.size()+3); codegen.opcodes[jump_fail_pos]=codegen.opcodes.size(); codegen.opcodes[jump_fail_pos2]=codegen.opcodes.size(); codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN_FALSE); codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS; } break; case GDParser::OperatorNode::OP_OR: { // OR operator with early out on success int res = _parse_expression(codegen,on->arguments[0],p_stack_level); if (res<0) return res; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF); codegen.opcodes.push_back(res); int jump_success_pos=codegen.opcodes.size(); codegen.opcodes.push_back(0); res = _parse_expression(codegen,on->arguments[1],p_stack_level); if (res<0) return res; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF); codegen.opcodes.push_back(res); int jump_success_pos2=codegen.opcodes.size(); codegen.opcodes.push_back(0); codegen.alloc_stack(p_stack_level); //it will be used.. codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN_FALSE); codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(codegen.opcodes.size()+3); codegen.opcodes[jump_success_pos]=codegen.opcodes.size(); codegen.opcodes[jump_success_pos2]=codegen.opcodes.size(); codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN_TRUE); codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS; } break; //unary operators case GDParser::OperatorNode::OP_NEG: { if (!_create_unary_operator(codegen,on,Variant::OP_NEGATE,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_NOT: { if (!_create_unary_operator(codegen,on,Variant::OP_NOT,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_BIT_INVERT: { if (!_create_unary_operator(codegen,on,Variant::OP_BIT_NEGATE,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_PREINC: { } break; //? case GDParser::OperatorNode::OP_PREDEC: { } break; case GDParser::OperatorNode::OP_INC: { } break; case GDParser::OperatorNode::OP_DEC: { } break; //binary operators (in precedence order) case GDParser::OperatorNode::OP_IN: { if (!_create_binary_operator(codegen,on,Variant::OP_IN,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_EQUAL: { if (!_create_binary_operator(codegen,on,Variant::OP_EQUAL,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_NOT_EQUAL: { if (!_create_binary_operator(codegen,on,Variant::OP_NOT_EQUAL,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_LESS: { if (!_create_binary_operator(codegen,on,Variant::OP_LESS,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_LESS_EQUAL: { if (!_create_binary_operator(codegen,on,Variant::OP_LESS_EQUAL,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_GREATER: { if (!_create_binary_operator(codegen,on,Variant::OP_GREATER,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_GREATER_EQUAL: { if (!_create_binary_operator(codegen,on,Variant::OP_GREATER_EQUAL,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_ADD: { if (!_create_binary_operator(codegen,on,Variant::OP_ADD,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_SUB: { if (!_create_binary_operator(codegen,on,Variant::OP_SUBSTRACT,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_MUL: { if (!_create_binary_operator(codegen,on,Variant::OP_MULTIPLY,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_DIV: { if (!_create_binary_operator(codegen,on,Variant::OP_DIVIDE,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_MOD: { if (!_create_binary_operator(codegen,on,Variant::OP_MODULE,p_stack_level)) return -1;} break; //case GDParser::OperatorNode::OP_SHIFT_LEFT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_LEFT,p_stack_level)) return -1;} break; //case GDParser::OperatorNode::OP_SHIFT_RIGHT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_RIGHT,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_BIT_AND: { if (!_create_binary_operator(codegen,on,Variant::OP_BIT_AND,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_BIT_OR: { if (!_create_binary_operator(codegen,on,Variant::OP_BIT_OR,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_BIT_XOR: { if (!_create_binary_operator(codegen,on,Variant::OP_BIT_XOR,p_stack_level)) return -1;} break; //shift case GDParser::OperatorNode::OP_SHIFT_LEFT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_LEFT,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_SHIFT_RIGHT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_RIGHT,p_stack_level)) return -1;} break; //assignment operators case GDParser::OperatorNode::OP_ASSIGN_ADD: case GDParser::OperatorNode::OP_ASSIGN_SUB: case GDParser::OperatorNode::OP_ASSIGN_MUL: case GDParser::OperatorNode::OP_ASSIGN_DIV: case GDParser::OperatorNode::OP_ASSIGN_MOD: case GDParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT: case GDParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT: case GDParser::OperatorNode::OP_ASSIGN_BIT_AND: case GDParser::OperatorNode::OP_ASSIGN_BIT_OR: case GDParser::OperatorNode::OP_ASSIGN_BIT_XOR: case GDParser::OperatorNode::OP_ASSIGN: { ERR_FAIL_COND_V(on->arguments.size()!=2,-1); if (on->arguments[0]->type==GDParser::Node::TYPE_OPERATOR && (static_cast<GDParser::OperatorNode*>(on->arguments[0])->op==GDParser::OperatorNode::OP_INDEX || static_cast<GDParser::OperatorNode*>(on->arguments[0])->op==GDParser::OperatorNode::OP_INDEX_NAMED)) { //SET (chained) MODE!! int slevel=p_stack_level; GDParser::OperatorNode* op = static_cast<GDParser::OperatorNode*>(on->arguments[0]); /* Find chain of sets */ List<GDParser::OperatorNode*> chain; { //create get/set chain GDParser::OperatorNode* n=op; while(true) { chain.push_back(n); if (n->arguments[0]->type!=GDParser::Node::TYPE_OPERATOR) break; n = static_cast<GDParser::OperatorNode*>(n->arguments[0]); if (n->op!=GDParser::OperatorNode::OP_INDEX && n->op!=GDParser::OperatorNode::OP_INDEX_NAMED) break; } } /* Chain of gets */ //get at (potential) root stack pos, so it can be returned int prev_pos = _parse_expression(codegen,chain.back()->get()->arguments[0],slevel); if (prev_pos<0) return prev_pos; int retval=prev_pos; //print_line("retval: "+itos(retval)); if (retval&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } Vector<int> setchain; int prev_key_idx=-1; for(List<GDParser::OperatorNode*>::Element *E=chain.back();E;E=E->prev()) { if (E==chain.front()) //ignore first break; bool named = E->get()->op==GDParser::OperatorNode::OP_INDEX_NAMED; int key_idx; if (named) { key_idx = codegen.get_name_map_pos(static_cast<const GDParser::IdentifierNode*>(E->get()->arguments[1])->name); //printf("named key %x\n",key_idx); } else { if (prev_pos&(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS)) { slevel++; codegen.alloc_stack(slevel); } GDParser::Node *key = E->get()->arguments[1]; key_idx = _parse_expression(codegen,key,slevel); //printf("expr key %x\n",key_idx); //stack was raised here if retval was stack but.. } if (key_idx<0) return key_idx; codegen.opcodes.push_back(named ? GDFunction::OPCODE_GET_NAMED : GDFunction::OPCODE_GET); codegen.opcodes.push_back(prev_pos); codegen.opcodes.push_back(key_idx); slevel++; codegen.alloc_stack(slevel); int dst_pos = (GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS)|slevel; codegen.opcodes.push_back(dst_pos); //add in reverse order, since it will be reverted setchain.push_back(dst_pos); setchain.push_back(key_idx); setchain.push_back(prev_pos); setchain.push_back(named ? GDFunction::OPCODE_SET_NAMED : GDFunction::OPCODE_SET); prev_pos=dst_pos; prev_key_idx=key_idx; } setchain.invert(); int set_index; bool named=false; if (static_cast<const GDParser::OperatorNode*>(op)->op==GDParser::OperatorNode::OP_INDEX_NAMED) { set_index=codegen.get_name_map_pos(static_cast<const GDParser::IdentifierNode*>(op->arguments[1])->name); named=true; } else { set_index = _parse_expression(codegen,op->arguments[1],slevel+1); named=false; } if (set_index<0) return set_index; if (set_index&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } int set_value = _parse_assign_right_expression(codegen,on,slevel+1); if (set_value<0) return set_value; codegen.opcodes.push_back(named?GDFunction::OPCODE_SET_NAMED:GDFunction::OPCODE_SET); codegen.opcodes.push_back(prev_pos); codegen.opcodes.push_back(set_index); codegen.opcodes.push_back(set_value); for(int i=0;i<setchain.size();i+=4) { codegen.opcodes.push_back(setchain[i+0]); codegen.opcodes.push_back(setchain[i+1]); codegen.opcodes.push_back(setchain[i+2]); codegen.opcodes.push_back(setchain[i+3]); } return retval; } else { //ASSIGNMENT MODE!! int slevel = p_stack_level; int dst_address_a = _parse_expression(codegen,on->arguments[0],slevel); if (dst_address_a<0) return -1; if (dst_address_a&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { slevel++; codegen.alloc_stack(slevel); } int src_address_b = _parse_assign_right_expression(codegen,on,slevel); if (src_address_b<0) return -1; codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN); // perform operator codegen.opcodes.push_back(dst_address_a); // argument 1 codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter) return dst_address_a; //if anything, returns wathever was assigned or correct stack position } } break; case GDParser::OperatorNode::OP_EXTENDS: { ERR_FAIL_COND_V(on->arguments.size()!=2,false); int slevel = p_stack_level; int src_address_a = _parse_expression(codegen,on->arguments[0],slevel); if (src_address_a<0) return -1; if (src_address_a&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) slevel++; //uses stack for return, increase stack int src_address_b = _parse_expression(codegen,on->arguments[1],slevel); if (src_address_b<0) return -1; codegen.opcodes.push_back(GDFunction::OPCODE_EXTENDS_TEST); // perform operator codegen.opcodes.push_back(src_address_a); // argument 1 codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter) } break; default: { ERR_EXPLAIN("Bug in bytecode compiler, unexpected operator #"+itos(on->op)+" in parse tree while parsing expression."); ERR_FAIL_V(0); //unreachable code } break; } int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode codegen.alloc_stack(p_stack_level); return dst_addr; } break; //TYPE_TYPE, default: { ERR_EXPLAIN("Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); ERR_FAIL_V(-1); //unreachable code } break; } ERR_FAIL_V(-1); //unreachable code }
Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class) { p_script->native=Ref<GDNativeClass>(); p_script->base=Ref<GDScript>(); p_script->_base=NULL; p_script->members.clear(); p_script->constants.clear(); p_script->member_functions.clear(); p_script->member_indices.clear(); p_script->member_info.clear(); p_script->initializer=NULL; p_script->subclasses.clear(); p_script->_owner=p_owner; p_script->tool=p_class->tool; p_script->name=p_class->name; int index_from=0; if (p_class->extends_used) { //do inheritance String path = p_class->extends_file; Ref<GDScript> script; Ref<GDNativeClass> native; if (path!="") { //path (and optionally subclasses) if (path.is_rel_path()) { String base = p_script->get_path(); if (base=="" || base.is_rel_path()) { _set_error("Could not resolve relative path for parent class: "+path,p_class); return ERR_FILE_NOT_FOUND; } path=base.get_base_dir().plus_file(path); } script = ResourceLoader::load(path); if (script.is_null()) { _set_error("Could not load base class: "+path,p_class); return ERR_FILE_NOT_FOUND; } if (!script->valid) { _set_error("Script not fully loaded (cyclic preload?): "+path,p_class); return ERR_BUSY; } //print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid)); if (p_class->extends_class.size()) { for(int i=0;i<p_class->extends_class.size();i++) { String sub = p_class->extends_class[i]; if (script->subclasses.has(sub)) { script=script->subclasses[sub]; } else { _set_error("Could not find subclass: "+sub,p_class); return ERR_FILE_NOT_FOUND; } } } } else { ERR_FAIL_COND_V(p_class->extends_class.size()==0,ERR_BUG); //look around for the subclasses String base=p_class->extends_class[0]; GDScript *p = p_owner; Ref<GDScript> base_class; while(p) { if (p->subclasses.has(base)) { base_class=p->subclasses[base]; break; } p=p->_owner; } if (base_class.is_valid()) { for(int i=1;i<p_class->extends_class.size();i++) { String subclass=p_class->extends_class[i]; if (base_class->subclasses.has(subclass)) { base_class=base_class->subclasses[subclass]; } else { _set_error("Could not find subclass: "+subclass,p_class); return ERR_FILE_NOT_FOUND; } } script=base_class; } else { if (p_class->extends_class.size()>1) { _set_error("Invalid inheritance (unknown class+subclasses)",p_class); return ERR_FILE_NOT_FOUND; } //if not found, try engine classes if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) { _set_error("Unknown class: '"+base+"'",p_class); return ERR_FILE_NOT_FOUND; } int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base]; native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx]; if (!native.is_valid()) { _set_error("Global not a class: '"+base+"'",p_class); return ERR_FILE_NOT_FOUND; } } } if (script.is_valid()) { p_script->base=script; p_script->_base=p_script->base.ptr(); p_script->member_indices=script->member_indices; } else if (native.is_valid()) { p_script->native=native; } else { _set_error("Could not determine inheritance",p_class); return ERR_FILE_NOT_FOUND; } } //print_line("Script: "+p_script->get_path()+" indices: "+itos(p_script->member_indices.size())); for(int i=0;i<p_class->variables.size();i++) { StringName name = p_class->variables[i].identifier; if (p_script->member_indices.has(name)) { _set_error("Member '"+name+"' already exists (in current or parent class)",p_class); return ERR_ALREADY_EXISTS; } if (p_class->variables[i]._export.type!=Variant::NIL) { p_script->member_info[name]=p_class->variables[i]._export; #ifdef TOOLS_ENABLED if (p_class->variables[i].default_value.get_type()!=Variant::NIL) { p_script->member_default_values[name]=p_class->variables[i].default_value; } #endif } int new_idx = p_script->member_indices.size(); p_script->member_indices[name]=new_idx; p_script->members.insert(name); } for(int i=0;i<p_class->constant_expressions.size();i++) { StringName name = p_class->constant_expressions[i].identifier; ERR_CONTINUE( p_class->constant_expressions[i].expression->type!=GDParser::Node::TYPE_CONSTANT ); GDParser::ConstantNode *constant = static_cast<GDParser::ConstantNode*>(p_class->constant_expressions[i].expression); p_script->constants.insert(name,constant->value); //p_script->constants[constant->value].make_const(); } //parse sub-classes for(int i=0;i<p_class->subclasses.size();i++) { StringName name = p_class->subclasses[i]->name; Ref<GDScript> subclass = memnew( GDScript ); Error err = _parse_class(subclass.ptr(),p_script,p_class->subclasses[i]); if (err) return err; p_script->constants.insert(name,subclass); //once parsed, goes to the list of constants p_script->subclasses.insert(name,subclass); } //parse methods bool has_initializer=false; for(int i=0;i<p_class->functions.size();i++) { if (!has_initializer && p_class->functions[i]->name=="_init") has_initializer=true; Error err = _parse_function(p_script,p_class,p_class->functions[i]); if (err) return err; } //parse static methods for(int i=0;i<p_class->static_functions.size();i++) { Error err = _parse_function(p_script,p_class,p_class->static_functions[i]); if (err) return err; } if (!has_initializer) { //create a constructor Error err = _parse_function(p_script,p_class,NULL); if (err) return err; } return OK; }
static bool _parse_completion_class(const String& p_base_path,const GDParser::ClassNode *p_class,int p_line,List<String>* r_options,List<String>::Element *p_indices) { static const char*_type_names[Variant::VARIANT_MAX]={ "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Trasnform", "Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray", "Vector2Array","Vector3Array","ColorArray"}; if (p_indices && !p_indices->next()) { for(int i=0;i<Variant::VARIANT_MAX;i++) { if (p_indices->get()==_type_names[i]) { List<StringName> ic; Variant::get_numeric_constants_for_type(Variant::Type(i),&ic); for(List<StringName>::Element *E=ic.front();E;E=E->next()) { r_options->push_back(E->get()); } return true; } } } for(int i=0;i<p_class->subclasses.size();i++) { if (p_line>=p_class->subclasses[i]->line && (p_line<=p_class->subclasses[i]->end_line || p_class->subclasses[i]->end_line==-1)) { if (_parse_completion_class(p_base_path,p_class->subclasses[i],p_line,r_options,p_indices)) return true; } } bool in_static_func=false; for(int i=0;i<p_class->functions.size();i++) { const GDParser::FunctionNode *fu = p_class->functions[i]; if (p_line>=fu->body->line && (p_line<=fu->body->end_line || fu->body->end_line==-1)) { //if in function, first block stuff from outer to inner if (_parse_completion_block(fu->body,p_line,r_options,p_indices)) return true; //then function arguments if (!p_indices) { for(int j=0;j<fu->arguments.size();j++) { r_options->push_back(fu->arguments[j]); } } } } for(int i=0;i<p_class->static_functions.size();i++) { const GDParser::FunctionNode *fu = p_class->static_functions[i]; if (p_line>=fu->body->line && (p_line<=fu->body->end_line || fu->body->end_line==-1)) { //if in function, first block stuff from outer to inne if (_parse_completion_block(fu->body,p_line,r_options,p_indices)) return true; //then function arguments if (!p_indices) { for(int j=0;j<fu->arguments.size();j++) { r_options->push_back(fu->arguments[j]); } } in_static_func=true; } } //add all local names if (!p_indices) { if (!in_static_func) { for(int i=0;i<p_class->variables.size();i++) { r_options->push_back(p_class->variables[i].identifier); } } for(int i=0;i<p_class->constant_expressions.size();i++) { r_options->push_back(p_class->constant_expressions[i].identifier); } if (!in_static_func) { for(int i=0;i<p_class->functions.size();i++) { r_options->push_back(p_class->functions[i]->name); } } for(int i=0;i<p_class->static_functions.size();i++) { r_options->push_back(p_class->static_functions[i]->name); } } if (p_class->extends_used) { //do inheritance String path = p_class->extends_file; Ref<GDScript> script; Ref<GDNativeClass> native; if (path!="") { //path (and optionally subclasses) script = ResourceLoader::load(path); if (script.is_null()) { return false; } if (p_class->extends_class.size()) { for(int i=0;i<p_class->extends_class.size();i++) { String sub = p_class->extends_class[i]; if (script->get_subclasses().has(sub)) { script=script->get_subclasses()[sub]; } else { return false; } } } } else { ERR_FAIL_COND_V(p_class->extends_class.size()==0,false); //look around for the subclasses String base=p_class->extends_class[0]; Ref<GDScript> base_class; #if 0 while(p) { if (p->subclasses.has(base)) { base_class=p->subclasses[base]; break; } p=p->_owner; } if (base_class.is_valid()) { for(int i=1;i<p_class->extends_class.size();i++) { String subclass=p_class->extends_class[i]; if (base_class->subclasses.has(subclass)) { base_class=base_class->subclasses[subclass]; } else { _set_error("Could not find subclass: "+subclass,p_class); return ERR_FILE_NOT_FOUND; } } } else { #endif if (p_class->extends_class.size()>1) { return false; } //if not found, try engine classes if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) { return false; } int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base]; native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx]; if (!native.is_valid()) { return false; } #if 0 } #endif } if (script.is_valid()) { if (_parse_script_symbols(script,in_static_func,r_options,p_indices)) return true; } else if (native.is_valid() && !p_indices) { _parse_native_symbols(native->get_name(),in_static_func,r_options); } } return false; }