/* * $Id$ * Copyright (c) 2014-2016 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "stix-prv.h" #define CLASS_BUFFER_ALIGN 64 #define LITERAL_BUFFER_ALIGN 64 #define CODE_BUFFER_ALIGN 512 #define BALIT_BUFFER_ALIGN 64 #define ARLIT_BUFFER_ALIGN 64 #define BLK_TMPRCNT_BUFFER_ALIGN 32 #define POOLDIC_OOP_BUFFER_ALIGN 32 /* initial method dictionary size */ #define INSTANCE_METHOD_DICTIONARY_SIZE 256 /* TODO: choose the right size */ #define CLASS_METHOD_DICTIONARY_SIZE 128 /* TODO: choose the right size */ #define NAMESPACE_SIZE 128 /* TODO: choose the right size */ #define POOL_DICTIONARY_SIZE_ALIGN 128 enum class_mod_t { CLASS_INDEXED = (1 << 0) }; enum var_type_t { /* NEVER Change the order and the value of 3 items below. * stix->c->cls.vars and stix->c->cls.var_count relies on them. */ VAR_INSTANCE = 0, VAR_CLASS = 1, VAR_CLASSINST = 2, /* NEVER Change the order and the value of 3 items above. */ VAR_GLOBAL, VAR_ARGUMENT, VAR_TEMPORARY }; typedef enum var_type_t var_type_t; struct var_info_t { var_type_t type; stix_ooi_t pos; /* not used for VAR_GLOBAL */ stix_oop_class_t cls; /* useful if type is VAR_CLASS. note STIX_NULL indicates the self class. */ stix_oop_association_t gbl; /* used for VAR_GLOBAL only */ }; typedef struct var_info_t var_info_t; static struct voca_t { stix_oow_t len; stix_ooch_t str[11]; } vocas[] = { { 5, { '#','b','y','t','e' } }, { 10, { '#','c','h','a','r','a','c','t','e','r' } }, { 5, { 'c','l','a','s','s' } }, { 6, { '#','c','l','a','s','s' } }, { 10, { '#','c','l','a','s','s','i','n','s','t' } }, { 3, { 'd','c','l' } }, { 7, { 'd','e','c','l','a','r','e' } }, { 6, { 'e','n','s','u','r','e', } }, { 5, { 'e','r','r','o','r' } }, { 9, { 'e','x','c','e','p','t','i','o','n' } }, { 6, { 'e','x','t','e','n','d' } }, { 5, { 'f','a','l','s','e' } }, { 4, { 'f','r','o','m' } }, { 9, { '#','h','a','l','f','w','o','r','d' } }, { 8, { '#','i','n','c','l','u','d','e' } }, { 7, { '#','l','i','w','o','r','d' } }, { 6, { 'm','e','t','h','o','d' } }, { 3, { 'n','i','l' } }, { 8, { '#','p','o','i','n','t','e','r' } }, { 7, { 'p','o','o','l','d','i','c' } }, { 8, { '#','p','o','o','l','d','i','c' } }, { 10, { 'p','r','i','m','i','t','i','v','e',':' } }, { 4, { 's','e','l','f' } }, { 5, { 's','u','p','e','r' } }, { 11, { 't','h','i','s','C','o','n','t','e','x','t' } }, { 11, { 't','h','i','s','P','r','o','c','e','s','s' } }, { 4, { 't','r','u','e' } }, { 5, { '#','w','o','r','d' } }, { 1, { '|' } }, { 1, { '>' } }, { 1, { '<' } }, { 5, { '<','E','O','F','>' } } }; enum voca_id_t { VOCA_BYTE_S, VOCA_CHARACTER_S, VOCA_CLASS, VOCA_CLASS_S, VOCA_CLASSINST_S, VOCA_DCL, VOCA_DECLARE, VOCA_ENSURE, VOCA_ERROR, VOCA_EXCEPTION, VOCA_EXTEND, VOCA_FALSE, VOCA_FROM, VOCA_HALFWORD_S, VOCA_INCLUDE_S, VOCA_LIWORD_S, VOCA_METHOD, VOCA_NIL, VOCA_POINTER_S, VOCA_POOLDIC, VOCA_POOLDIC_S, VOCA_PRIMITIVE_COLON, VOCA_SELF, VOCA_SUPER, VOCA_THIS_CONTEXT, VOCA_THIS_PROCESS, VOCA_TRUE, VOCA_WORD_S, VOCA_VBAR, VOCA_GT, VOCA_LT, VOCA_EOF }; typedef enum voca_id_t voca_id_t; static int compile_block_statement (stix_t* stix); static int compile_method_statement (stix_t* stix); static int compile_method_expression (stix_t* stix, int pop); static int add_literal (stix_t* stix, stix_oop_t lit, stix_oow_t* index); static STIX_INLINE int is_spacechar (stix_ooci_t c) { /* TODO: handle other space unicode characters */ switch (c) { case ' ': case '\f': /* formfeed */ case '\n': /* linefeed */ case '\r': /* carriage return */ case '\t': /* horizon tab */ case '\v': /* vertical tab */ return 1; default: return 0; } } static STIX_INLINE int is_alphachar (stix_ooci_t c) { /* TODO: support full unicode */ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } static STIX_INLINE int is_digitchar (stix_ooci_t c) { /* TODO: support full unicode */ return (c >= '0' && c <= '9'); } static STIX_INLINE int is_alnumchar (stix_ooci_t c) { /* TODO: support full unicode */ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } static STIX_INLINE int is_binselchar (stix_ooci_t c) { /* * binary-selector-character := * '%' | '&' | '*' | '+' | '-' | * '/' | '<' | '>' | '=' | '?' | * '@' | '\' | '~' | '|' */ switch (c) { case '%': case '&': case '*': case '+': case '-': case '/': case '<': case '>': case '=': case '?': case '@': case '\\': case '|': case '~': return 1; default: return 0; } } static STIX_INLINE int is_leadidentchar (stix_ooci_t c) { return is_alphachar(c) || c == '_'; } static STIX_INLINE int is_identchar (stix_ooci_t c) { return is_alnumchar(c) || c == '_'; } static STIX_INLINE int is_closing_char (stix_ooci_t c) { switch (c) { case '.': case ']': case ')': case ';': case '\"': case '\'': return 1; default: return 0; } } static STIX_INLINE int is_word (const stix_oocs_t* oocs, voca_id_t id) { return oocs->len == vocas[id].len && stix_equaloochars(oocs->ptr, vocas[id].str, vocas[id].len); } static int is_reserved_word (const stix_oocs_t* ucs) { static int rw[] = { VOCA_SELF, VOCA_SUPER, VOCA_NIL, VOCA_TRUE, VOCA_FALSE, VOCA_ERROR, VOCA_THIS_CONTEXT, VOCA_THIS_PROCESS, }; int i; for (i = 0; i < STIX_COUNTOF(rw); i++) { if (is_word(ucs, rw[i])) return 1; } return 0; } static int is_restricted_word (const stix_oocs_t* ucs) { /* not fully reserved. but restricted in a certain context */ static int rw[] = { VOCA_CLASS, VOCA_DCL, VOCA_DECLARE, VOCA_EXTEND, VOCA_METHOD, VOCA_POOLDIC, VOCA_FROM }; int i; for (i = 0; i < STIX_COUNTOF(rw); i++) { if (is_word(ucs, rw[i])) return 1; } return 0; } static int begin_include (stix_t* stix); static int end_include (stix_t* stix); static void set_syntax_error (stix_t* stix, stix_synerrnum_t num, const stix_ioloc_t* loc, const stix_oocs_t* tgt) { stix->errnum = STIX_ESYNTAX; stix->c->synerr.num = num; /* The SCO compiler complains of this ternary operation saying: * error: operands have incompatible types: op ":" * it seems to complain of type mismatch between *loc and * stix->c->tok.loc due to 'const' prefixed to loc. */ /*stix->c->synerr.loc = loc? *loc: stix->c->tok.loc;*/ if (loc) stix->c->synerr.loc = *loc; else stix->c->synerr.loc = stix->c->tok.loc; if (tgt) stix->c->synerr.tgt = *tgt; else { stix->c->synerr.tgt.ptr = STIX_NULL; stix->c->synerr.tgt.len = 0; } } static int copy_string_to (stix_t* stix, const stix_oocs_t* src, stix_oocs_t* dst, stix_oow_t* dst_capa, int append, stix_ooch_t add_delim) { stix_oow_t len, pos; if (append) { pos = dst->len; len = dst->len + src->len; if (add_delim != '\0') len++; } else { pos = 0; len = src->len; } if (len > *dst_capa) { stix_ooch_t* tmp; stix_oow_t capa; capa = STIX_ALIGN(len, CLASS_BUFFER_ALIGN); tmp = stix_reallocmem (stix, dst->ptr, STIX_SIZEOF(*tmp) * capa); if (!tmp) return -1; dst->ptr = tmp; *dst_capa = capa; } if (append && add_delim) dst->ptr[pos++] = add_delim; stix_copyoochars (&dst->ptr[pos], src->ptr, src->len); dst->len = len; return 0; } static int find_word_in_string (const stix_oocs_t* haystack, const stix_oocs_t* name, stix_oow_t* xindex) { /* this function is inefficient. but considering the typical number * of arguments and temporary variables, the inefficiency can be * ignored in my opinion. the overhead to maintain the reverse lookup * table from a name to an index should be greater than this simple * inefficient lookup */ stix_ooch_t* t, * e; stix_oow_t index, i; t = haystack->ptr; e = t + haystack->len; index = 0; while (t < e) { while (t < e && is_spacechar(*t)) t++; for (i = 0; i < name->len; i++) { if (t >= e || name->ptr[i] != *t) goto unmatched; t++; } if (t >= e || is_spacechar(*t)) { if (xindex) *xindex = index; return 0; } unmatched: while (t < e) { if (is_spacechar(*t)) { t++; break; } t++; } index++; } return -1; } #define CHAR_TO_NUM(c,base) \ ((c >= '0' && c <= '9')? ((c - '0' < base)? (c - '0'): base): \ (c >= 'A' && c <= 'Z')? ((c - 'A' + 10 < base)? (c - 'A' + 10): base): \ (c >= 'a' && c <= 'z')? ((c - 'a' + 10 < base)? (c - 'a' + 10): base): base) static int string_to_smooi (stix_t* stix, stix_oocs_t* str, int radixed, stix_ooi_t* num) { /* it is not a generic conversion function. * it assumes a certain pre-sanity check on the string * done by the lexical analyzer */ int v, negsign, base; const stix_ooch_t* ptr, * end; stix_oow_t value, old_value; negsign = 0; ptr = str->ptr, end = str->ptr + str->len; STIX_ASSERT (stix, ptr < end); if (*ptr == '+' || *ptr == '-') { negsign = *ptr - '+'; ptr++; } if (radixed) { STIX_ASSERT (stix, ptr < end); base = 0; do { base = base * 10 + CHAR_TO_NUM(*ptr, 10); ptr++; } while (*ptr != 'r'); ptr++; } else base = 10; STIX_ASSERT (stix, ptr < end); value = old_value = 0; while (ptr < end && (v = CHAR_TO_NUM(*ptr, base)) < base) { value = value * base + v; if (value < old_value) { /* overflow must have occurred */ stix->errnum = STIX_ERANGE; return -1; } old_value = value; ptr++; } if (ptr < end) { /* trailing garbage? */ stix->errnum = STIX_EINVAL; return -1; } STIX_ASSERT (stix, -STIX_SMOOI_MAX == STIX_SMOOI_MIN); if (value > STIX_SMOOI_MAX) { stix->errnum = STIX_ERANGE; return -1; } *num = value; if (negsign) *num *= -1; return 0; } static stix_oop_t string_to_num (stix_t* stix, stix_oocs_t* str, int radixed) { int negsign, base; const stix_ooch_t* ptr, * end; negsign = 0; ptr = str->ptr, end = str->ptr + str->len; STIX_ASSERT (stix, ptr < end); if (*ptr == '+' || *ptr == '-') { negsign = *ptr - '+'; ptr++; } if (radixed) { STIX_ASSERT (stix, ptr < end); base = 0; do { base = base * 10 + CHAR_TO_NUM(*ptr, 10); ptr++; } while (*ptr != 'r'); ptr++; } else base = 10; /* TODO: handle floating point numbers ... etc */ if (negsign) base = -base; return stix_strtoint (stix, ptr, end - ptr, base); } static stix_oop_t string_to_error (stix_t* stix, stix_oocs_t* str) { stix_ooi_t num = 0; const stix_ooch_t* ptr, * end; ptr = str->ptr, end = str->ptr + str->len; /* i assume that the input is in the form of error(NNN) * all other letters are non-digits except the NNN part. * i just skip all non-digit letters for simplicity sake. */ while (ptr < end) { if (is_digitchar(*ptr)) num = num * 10 + (*ptr - '0'); ptr++; } return STIX_ERROR_TO_OOP(num); } /* --------------------------------------------------------------------- * Tokenizer * --------------------------------------------------------------------- */ #define GET_CHAR(stix) \ do { if (get_char(stix) <= -1) return -1; } while (0) #define GET_CHAR_TO(stix,c) \ do { \ if (get_char(stix) <= -1) return -1; \ c = (stix)->c->lxc.c; \ } while(0) #define GET_TOKEN(stix) \ do { if (get_token(stix) <= -1) return -1; } while (0) #define GET_TOKEN_WITH_ERRRET(stix, v_ret) \ do { if (get_token(stix) <= -1) return v_ret; } while (0) #define ADD_TOKEN_STR(stix,s,l) \ do { if (add_token_str(stix, s, l) <= -1) return -1; } while (0) #define ADD_TOKEN_CHAR(stix,c) \ do { if (add_token_char(stix, c) <= -1) return -1; } while (0) #define CLEAR_TOKEN_NAME(stix) ((stix)->c->tok.name.len = 0) #define SET_TOKEN_TYPE(stix,tv) ((stix)->c->tok.type = (tv)) #define TOKEN_TYPE(stix) ((stix)->c->tok.type) #define TOKEN_NAME(stix) (&(stix)->c->tok.name) #define TOKEN_NAME_CAPA(stix) ((stix)->c->tok.name_capa) #define TOKEN_NAME_PTR(stix) ((stix)->c->tok.name.ptr) #define TOKEN_NAME_LEN(stix) ((stix)->c->tok.name.len) #define TOKEN_LOC(stix) (&(stix)->c->tok.loc) #define LEXER_LOC(stix) (&(stix)->c->lxc.l) static STIX_INLINE int does_token_name_match (stix_t* stix, voca_id_t id) { return TOKEN_NAME_LEN(stix) == vocas[id].len && stix_equaloochars(TOKEN_NAME_PTR(stix), vocas[id].str, vocas[id].len); } static STIX_INLINE int is_token_symbol (stix_t* stix, voca_id_t id) { return TOKEN_TYPE(stix) == STIX_IOTOK_SYMLIT && does_token_name_match(stix, id); } static STIX_INLINE int is_token_word (stix_t* stix, voca_id_t id) { return TOKEN_TYPE(stix) == STIX_IOTOK_IDENT && does_token_name_match(stix, id); } static STIX_INLINE int is_token_binary_selector (stix_t* stix, voca_id_t id) { return TOKEN_TYPE(stix) == STIX_IOTOK_BINSEL && does_token_name_match(stix, id); } static STIX_INLINE int is_token_keyword (stix_t* stix, voca_id_t id) { return TOKEN_TYPE(stix) == STIX_IOTOK_KEYWORD && does_token_name_match(stix, id); } static STIX_INLINE int add_token_str (stix_t* stix, const stix_ooch_t* ptr, stix_oow_t len) { stix_oocs_t tmp; tmp.ptr = (stix_ooch_t*)ptr; tmp.len = len; return copy_string_to (stix, &tmp, TOKEN_NAME(stix), &TOKEN_NAME_CAPA(stix), 1, '\0'); } static STIX_INLINE int add_token_char (stix_t* stix, stix_ooch_t c) { stix_oocs_t tmp; tmp.ptr = &c; tmp.len = 1; return copy_string_to (stix, &tmp, TOKEN_NAME(stix), &TOKEN_NAME_CAPA(stix), 1, '\0'); } static STIX_INLINE void unget_char (stix_t* stix, const stix_iolxc_t* c) { /* Make sure that the unget buffer is large enough */ STIX_ASSERT (stix, stix->c->nungots < STIX_COUNTOF(stix->c->ungot)); stix->c->ungot[stix->c->nungots++] = *c; } static int get_char (stix_t* stix) { stix_ooi_t n; stix_ooci_t lc, ec; if (stix->c->nungots > 0) { /* something in the unget buffer */ stix->c->lxc = stix->c->ungot[--stix->c->nungots]; return 0; } if (stix->c->curinp->b.state == -1) { stix->c->curinp->b.state = 0; return -1; } else if (stix->c->curinp->b.state == 1) { stix->c->curinp->b.state = 0; goto return_eof; } if (stix->c->curinp->b.pos >= stix->c->curinp->b.len) { n = stix->c->impl (stix, STIX_IO_READ, stix->c->curinp); if (n <= -1) return -1; if (n == 0) { return_eof: stix->c->curinp->lxc.c = STIX_UCI_EOF; stix->c->curinp->lxc.l.line = stix->c->curinp->line; stix->c->curinp->lxc.l.colm = stix->c->curinp->colm; stix->c->curinp->lxc.l.file = stix->c->curinp->name; stix->c->lxc = stix->c->curinp->lxc; /* indicate that EOF has been read. lxc.c is also set to EOF. */ return 0; } stix->c->curinp->b.pos = 0; stix->c->curinp->b.len = n; } if (stix->c->curinp->lxc.c == '\n' || stix->c->curinp->lxc.c == '\r') { /* stix->c->curinp->lxc.c is a previous character. the new character * to be read is still in the buffer (stix->c->curinp->buf). * stix->cu->curinp->colm has been incremented when the previous * character has been read. */ if (stix->c->curinp->line > 1 && stix->c->curinp->colm == 2 && stix->c->curinp->nl != stix->c->curinp->lxc.c) { /* most likely, it's the second character in '\r\n' or '\n\r' * sequence. let's not update the line and column number. */ /*stix->c->curinp->colm = 1;*/ } else { /* if the previous charater was a newline, * increment the line counter and reset column to 1. * incrementing the line number here instead of * updating inp->lxc causes the line number for * TOK_EOF to be the same line as the lxc newline. */ stix->c->curinp->line++; stix->c->curinp->colm = 1; stix->c->curinp->nl = stix->c->curinp->lxc.c; } } lc = stix->c->curinp->buf[stix->c->curinp->b.pos++]; stix->c->curinp->lxc.c = lc; stix->c->curinp->lxc.l.line = stix->c->curinp->line; stix->c->curinp->lxc.l.colm = stix->c->curinp->colm++; stix->c->curinp->lxc.l.file = stix->c->curinp->name; stix->c->lxc = stix->c->curinp->lxc; return 1; /* indicate that a normal character has been read */ } static STIX_INLINE int skip_spaces (stix_t* stix) { while (is_spacechar(stix->c->lxc.c)) GET_CHAR (stix); return 0; } static int skip_comment (stix_t* stix) { stix_ooci_t c = stix->c->lxc.c; stix_iolxc_t lc; if (c == '"') { /* skip up to the closing " */ do { GET_CHAR_TO (stix, c); if (c == STIX_UCI_EOF) goto unterminated; } while (c != '"'); if (c == '"') GET_CHAR (stix); /* keep the next character in lxc */ return 1; /* double-quoted comment */ } else if (c == '(') { /* handle (* ... *) */ lc = stix->c->lxc; GET_CHAR_TO (stix, c); if (c != '*') goto not_comment; do { GET_CHAR_TO (stix, c); if (c == STIX_UCI_EOF) goto unterminated; if (c == '*') { GET_CHAR_TO (stix, c); if (c == STIX_UCI_EOF) goto unterminated; if (c == ')') { GET_CHAR (stix); /* keep the first meaningful character in lxc */ break; } } } while (1); return 1; /* multi-line comment enclosed in (* and *) */ } else if (c == '#') { /* handle #! or ## */ /* save the last character */ lc = stix->c->lxc; /* read a new character */ GET_CHAR_TO (stix, c); if (c != '!' && c != '#') goto not_comment; do { GET_CHAR_TO (stix, c); if (c == STIX_UCI_EOF) { /* EOF on the comment line is ok for a single-line comment */ break; } else if (c == '\r' || c == '\n') { GET_CHAR (stix); /* keep the first meaningful character in lxc */ break; } } while (1); return 1; /* single line comment led by ## or #! */ } else { /* not comment. but no next character has been consumed. * no need to unget a character */ return 0; } not_comment: /* unget the leading '#' */ unget_char (stix, &stix->c->lxc); /* restore the previous state */ stix->c->lxc = lc; return 0; unterminated: set_syntax_error (stix, STIX_SYNERR_CMTNC, LEXER_LOC(stix), STIX_NULL); return -1; } static int get_ident (stix_t* stix, stix_ooci_t char_read_ahead) { /* * identifier := alpha-char (alpha-char | digit-char)* * keyword := identifier ":" */ stix_ooci_t c; c = stix->c->lxc.c; SET_TOKEN_TYPE (stix, STIX_IOTOK_IDENT); if (char_read_ahead != STIX_UCI_EOF) { ADD_TOKEN_CHAR(stix, char_read_ahead); } do { ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); } while (is_identchar(c)); if (c == '(' && is_token_word(stix, VOCA_ERROR)) { /* error(NN) */ ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); if (!is_digitchar(c)) { ADD_TOKEN_CHAR (stix, c); set_syntax_error (stix, STIX_SYNERR_ERRLIT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } do { ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); } while (is_digitchar(c)); if (c != ')') { set_syntax_error (stix, STIX_SYNERR_RPAREN, LEXER_LOC(stix), STIX_NULL); return -1; } /* TODO: error number range check */ ADD_TOKEN_CHAR (stix, c); SET_TOKEN_TYPE (stix, STIX_IOTOK_ERRLIT); } else if (c == ':') { read_more_kwsym: ADD_TOKEN_CHAR (stix, c); SET_TOKEN_TYPE (stix, STIX_IOTOK_KEYWORD); GET_CHAR_TO (stix, c); if (stix->c->in_array && is_leadidentchar(c)) { /* when reading an array literal, read as many characters as * would compose a normal keyword symbol literal */ do { ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); } while (is_identchar(c)); if (c == ':') goto read_more_kwsym; else { /* the last character is not a colon */ set_syntax_error (stix, STIX_SYNERR_COLON, LEXER_LOC(stix), STIX_NULL); return -1; } } else { unget_char (stix, &stix->c->lxc); } } else { if (c == '.') { stix_iolxc_t period; period = stix->c->lxc; read_more_seg: GET_CHAR_TO (stix, c); if (is_leadidentchar(c)) { SET_TOKEN_TYPE (stix, STIX_IOTOK_IDENT_DOTTED); ADD_TOKEN_CHAR (stix, '.'); do { ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); } while (is_identchar(c)); if (c == '.') goto read_more_seg; else unget_char (stix, &stix->c->lxc); } else { unget_char (stix, &stix->c->lxc); /* unget the period itself */ unget_char (stix, &period); } } else { unget_char (stix, &stix->c->lxc); } /* handle reserved words */ if (is_token_word(stix, VOCA_SELF)) { SET_TOKEN_TYPE (stix, STIX_IOTOK_SELF); } else if (is_token_word(stix, VOCA_SUPER)) { SET_TOKEN_TYPE (stix, STIX_IOTOK_SUPER); } else if (is_token_word(stix, VOCA_NIL)) { SET_TOKEN_TYPE (stix, STIX_IOTOK_NIL); } else if (is_token_word(stix, VOCA_TRUE)) { SET_TOKEN_TYPE (stix, STIX_IOTOK_TRUE); } else if (is_token_word(stix, VOCA_FALSE)) { SET_TOKEN_TYPE (stix, STIX_IOTOK_FALSE); } else if (is_token_word(stix, VOCA_ERROR)) { SET_TOKEN_TYPE (stix, STIX_IOTOK_ERROR); } else if (is_token_word(stix, VOCA_THIS_CONTEXT)) { SET_TOKEN_TYPE (stix, STIX_IOTOK_THIS_CONTEXT); } else if (is_token_word(stix, VOCA_THIS_PROCESS)) { SET_TOKEN_TYPE (stix, STIX_IOTOK_THIS_PROCESS); } } return 0; } static int get_numlit (stix_t* stix, int negated) { /* * number-literal := number | ("-" number) * number := integer | float | scaledDecimal * integer := decimal-integer | radix-integer * decimal-integer := digit-char+ * radix-integer := radix-specifier "r" radix-digit+ * radix-specifier := digit-char+ * radix-digit := digit-char | upper-alpha-char * * float := mantissa [exponentLetter exponent] * mantissa := digit-char+ "." digit-char+ * exponent := ['-'] decimal-integer * exponentLetter := 'e' | 'd' | 'q' * scaledDecimal := scaledMantissa 's' [fractionalDigits] * scaledMantissa := decimal-integer | mantissa * fractionalDigits := decimal-integer */ stix_ooci_t c; int radix = 0, r; c = stix->c->lxc.c; SET_TOKEN_TYPE (stix, STIX_IOTOK_NUMLIT); /*TODO: support a complex numeric literal */ do { if (radix <= 36) { /* collect the potential radix specifier */ r = CHAR_TO_NUM (c, 10); STIX_ASSERT (stix, r < 10); radix = radix * 10 + r; } ADD_TOKEN_CHAR(stix, c); GET_CHAR_TO (stix, c); if (c == '_') { stix_iolxc_t underscore; underscore = stix->c->lxc; GET_CHAR_TO(stix, c); if (!is_digitchar(c)) { unget_char (stix, &stix->c->lxc); unget_char (stix, &underscore); break; } else continue; } } while (is_digitchar(c)); if (c == 'r') { /* radix specifier */ if (radix < 2 || radix > 36) { /* no digit after the radix specifier */ set_syntax_error (stix, STIX_SYNERR_RADIX, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } ADD_TOKEN_CHAR(stix, c); GET_CHAR_TO (stix, c); if (CHAR_TO_NUM(c, radix) >= radix) { /* no digit after the radix specifier */ set_syntax_error (stix, STIX_SYNERR_RADNUMLIT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } do { ADD_TOKEN_CHAR(stix, c); GET_CHAR_TO (stix, c); if (c == '_') { stix_iolxc_t underscore; underscore = stix->c->lxc; GET_CHAR_TO(stix, c); if (CHAR_TO_NUM(c, radix) >= radix) { unget_char (stix, &stix->c->lxc); unget_char (stix, &underscore); break; } else continue; } } while (CHAR_TO_NUM(c, radix) < radix); SET_TOKEN_TYPE (stix, STIX_IOTOK_RADNUMLIT); } unget_char (stix, &stix->c->lxc); /* * TODO: handle floating point number */ return 0; } static int get_charlit (stix_t* stix) { /* * character-literal := "$" character * character := normal-character | "'" */ stix_ooci_t c = stix->c->lxc.c; /* even a new-line or white space would be taken */ if (c == STIX_UCI_EOF) { set_syntax_error (stix, STIX_SYNERR_CLTNT, LEXER_LOC(stix), STIX_NULL); return -1; } SET_TOKEN_TYPE (stix, STIX_IOTOK_CHARLIT); ADD_TOKEN_CHAR(stix, c); return 0; } static int get_strlit (stix_t* stix) { /* * string-literal := single-quote string-character* single-quote * string-character := normal-character | (single-quote single-quote) * single-quote := "'" * normal-character := character-except-single-quote */ stix_ooci_t c = stix->c->lxc.c; SET_TOKEN_TYPE (stix, STIX_IOTOK_STRLIT); do { do { ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); if (c == STIX_UCI_EOF) { /* string not closed */ set_syntax_error (stix, STIX_SYNERR_STRNC, TOKEN_LOC(stix) /*&stix->c->lxc.l*/, STIX_NULL); return -1; } } while (c != '\''); /* 'c' must be a single quote at this point*/ GET_CHAR_TO (stix, c); } while (c == '\''); /* if the next character is a single quote, it becomes a literal single quote character. */ unget_char (stix, &stix->c->lxc); return 0; } static int get_string (stix_t* stix, stix_ooch_t end_char, stix_ooch_t esc_char, int regex, stix_oow_t preescaped) { stix_ooci_t c; stix_oow_t escaped = preescaped; stix_oow_t digit_count = 0; stix_ooci_t c_acc = 0; SET_TOKEN_TYPE (stix, STIX_IOTOK_STRLIT); while (1) { GET_CHAR_TO (stix, c); if (c == STIX_UCI_EOF) { set_syntax_error (stix, STIX_SYNERR_STRNC, TOKEN_LOC(stix) /*&stix->c->lxc.l*/, STIX_NULL); return -1; } if (escaped == 3) { if (c >= '0' && c <= '7') { c_acc = c_acc * 8 + c - '0'; digit_count++; if (digit_count >= escaped) { /* should i limit the max to 0xFF/0377? * if (c_acc > 0377) c_acc = 0377;*/ ADD_TOKEN_CHAR (stix, c_acc); escaped = 0; } continue; } else { ADD_TOKEN_CHAR (stix, c_acc); escaped = 0; } } else if (escaped == 2 || escaped == 4 || escaped == 8) { if (c >= '0' && c <= '9') { c_acc = c_acc * 16 + c - '0'; digit_count++; if (digit_count >= escaped) { ADD_TOKEN_CHAR (stix, c_acc); escaped = 0; } continue; } else if (c >= 'A' && c <= 'F') { c_acc = c_acc * 16 + c - 'A' + 10; digit_count++; if (digit_count >= escaped) { ADD_TOKEN_CHAR (stix, c_acc); escaped = 0; } continue; } else if (c >= 'a' && c <= 'f') { c_acc = c_acc * 16 + c - 'a' + 10; digit_count++; if (digit_count >= escaped) { ADD_TOKEN_CHAR (stix, c_acc); escaped = 0; } continue; } else { stix_ooch_t rc; rc = (escaped == 2)? 'x': (escaped == 4)? 'u': 'U'; if (digit_count == 0) ADD_TOKEN_CHAR (stix, rc); else ADD_TOKEN_CHAR (stix, c_acc); escaped = 0; } } if (escaped == 0 && c == end_char) { /* terminating quote */ break; } if (escaped == 0 && c == esc_char) { escaped = 1; continue; } if (escaped == 1) { if (c == 'n') c = '\n'; else if (c == 'r') c = '\r'; else if (c == 't') c = '\t'; else if (c == 'f') c = '\f'; else if (c == 'b') c = '\b'; else if (c == 'v') c = '\v'; else if (c == 'a') c = '\a'; else if (c >= '0' && c <= '7' && !regex) { /* i don't support the octal notation for a regular expression. * it conflicts with the backreference notation between \1 and \7 inclusive. */ escaped = 3; digit_count = 1; c_acc = c - '0'; continue; } else if (c == 'x') { escaped = 2; digit_count = 0; c_acc = 0; continue; } else if (c == 'u' && STIX_SIZEOF(stix_ooch_t) >= 2) { escaped = 4; digit_count = 0; c_acc = 0; continue; } else if (c == 'U' && STIX_SIZEOF(stix_ooch_t) >= 4) { escaped = 8; digit_count = 0; c_acc = 0; continue; } else if (regex) { /* if the following character doesn't compose a proper * escape sequence, keep the escape character. * an unhandled escape sequence can be handled * outside this function since the escape character * is preserved.*/ ADD_TOKEN_CHAR (stix, esc_char); } escaped = 0; } ADD_TOKEN_CHAR (stix, c); } return 0; } static int get_binsel (stix_t* stix) { /* * binary-selector := binary-selector-character+ */ stix_ooci_t oc; oc = stix->c->lxc.c; ADD_TOKEN_CHAR (stix, oc); GET_CHAR (stix); /* special case if a minus is followed by a digit immediately */ if (oc == '-' && is_digitchar(stix->c->lxc.c)) return get_numlit (stix, 1); #if 1 /* up to 2 characters only */ if (is_binselchar (stix->c->lxc.c)) { ADD_TOKEN_CHAR (stix, stix->c->lxc.c); } else { unget_char (stix, &stix->c->lxc); } #else /* or up to any occurrences */ while (is_binselchar(stix->c->lxc.c)) { ADD_TOKEN_CHAR (stix, c); GET_CHAR (stix); } unget_char (stix, &stix->c->lxc); #endif SET_TOKEN_TYPE (stix, STIX_IOTOK_BINSEL); return 0; } static int get_token (stix_t* stix) { stix_ooci_t c; int n; retry: GET_CHAR (stix); do { /* skip spaces */ while (is_spacechar(stix->c->lxc.c)) GET_CHAR (stix); /* the first character after the last space is in stix->c->lxc */ if ((n = skip_comment(stix)) <= -1) return -1; } while (n >= 1); /* clear the token name, reset its location */ SET_TOKEN_TYPE (stix, STIX_IOTOK_EOF); /* is it correct? */ CLEAR_TOKEN_NAME (stix); stix->c->tok.loc = stix->c->lxc.l; /* remember token location */ c = stix->c->lxc.c; switch (c) { case STIX_UCI_EOF: { int n; n = end_include (stix); if (n <= -1) return -1; if (n >= 1) goto retry; SET_TOKEN_TYPE (stix, STIX_IOTOK_EOF); ADD_TOKEN_STR(stix, vocas[VOCA_EOF].str, vocas[VOCA_EOF].len); break; } case '$': /* character literal */ GET_CHAR (stix); if (get_charlit(stix) <= -1) return -1; break; case '\'': /* string literal */ GET_CHAR (stix); if (get_strlit(stix) <= -1) return -1; break; case ':': SET_TOKEN_TYPE (stix, STIX_IOTOK_COLON); ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); if (c == '=') { SET_TOKEN_TYPE (stix, STIX_IOTOK_ASSIGN); ADD_TOKEN_CHAR (stix, c); } else { unget_char (stix, &stix->c->lxc); } break; case '^': SET_TOKEN_TYPE (stix, STIX_IOTOK_RETURN); ADD_TOKEN_CHAR(stix, c); #if 0 GET_CHAR_TO (stix, c); /* TODO: support explicit block return */ if (c == '^') { /* ^^ */ TOKEN_TYPE(stix) == STIX_IOTOK_BLKRET; ADD_TOKEN_CHAR (stix, c); } else { unget_char (stix, &stix->c->lxc); } #endif break; case '{': /* extension */ SET_TOKEN_TYPE (stix, STIX_IOTOK_LBRACE); goto single_char_token; case '}': /* extension */ SET_TOKEN_TYPE (stix, STIX_IOTOK_RBRACE); goto single_char_token; case '[': SET_TOKEN_TYPE (stix, STIX_IOTOK_LBRACK); goto single_char_token; case ']': SET_TOKEN_TYPE (stix, STIX_IOTOK_RBRACK); goto single_char_token; case '(': SET_TOKEN_TYPE (stix, STIX_IOTOK_LPAREN); goto single_char_token; case ')': SET_TOKEN_TYPE (stix, STIX_IOTOK_RPAREN); goto single_char_token; case '.': SET_TOKEN_TYPE (stix, STIX_IOTOK_PERIOD); goto single_char_token; case ',': SET_TOKEN_TYPE (stix, STIX_IOTOK_COMMA); goto single_char_token; case ';': SET_TOKEN_TYPE (stix, STIX_IOTOK_SEMICOLON); goto single_char_token; case '#': ADD_TOKEN_CHAR(stix, c); GET_CHAR_TO (stix, c); switch (c) { case STIX_UCI_EOF: set_syntax_error (stix, STIX_SYNERR_HLTNT, LEXER_LOC(stix), STIX_NULL); return -1; case '(': /* #( */ ADD_TOKEN_CHAR(stix, c); SET_TOKEN_TYPE (stix, STIX_IOTOK_ARPAREN); break; case '[': /* #[ - byte array literal */ ADD_TOKEN_CHAR(stix, c); SET_TOKEN_TYPE (stix, STIX_IOTOK_BAPAREN); break; case '\'': /* quoted symbol literal */ GET_CHAR (stix); if (get_strlit(stix) <= -1) return -1; SET_TOKEN_TYPE (stix, STIX_IOTOK_SYMLIT); break; default: /* symbol-literal := "#" symbol-body * symbol-body := identifier | keyword+ | binary-selector | string-literal */ /* unquoted symbol literal */ if (is_binselchar(c)) { do { ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); } while (is_binselchar(c)); unget_char (stix, &stix->c->lxc); } else if (is_leadidentchar(c)) { do { ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); } while (is_identchar(c)); if (c == ':') { /* keyword symbol - e.g. #ifTrue:ifFalse: */ read_more_word: ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); if (is_leadidentchar(c)) { do { ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); } while (is_identchar(c)); if (c == ':') goto read_more_word; else { /* if a colon is found in the middle of a symbol, * the last charater is expected to be a colon as well. * but, the last character is not a colon */ set_syntax_error (stix, STIX_SYNERR_COLON, LEXER_LOC(stix), STIX_NULL); return -1; } } else { unget_char (stix, &stix->c->lxc); } } else if (c == '.') { /* dotted symbol e.g. #Planet.Earth.Object */ stix_iolxc_t period; period = stix->c->lxc; read_more_seg: GET_CHAR_TO (stix, c); if (is_leadidentchar(c)) { ADD_TOKEN_CHAR (stix, '.'); do { ADD_TOKEN_CHAR (stix, c); GET_CHAR_TO (stix, c); } while (is_identchar(c)); if (c == '.') goto read_more_seg; else unget_char (stix, &stix->c->lxc); } else { unget_char (stix, &stix->c->lxc); unget_char (stix, &period); } } else { unget_char (stix, &stix->c->lxc); } } else { set_syntax_error (stix, STIX_SYNERR_HLTNT, LEXER_LOC(stix), STIX_NULL); return -1; } SET_TOKEN_TYPE (stix, STIX_IOTOK_SYMLIT); break; } break; case 'C': /* a character with a C-style escape sequence */ case 'S': /* a string with a C-style escape sequences */ case 'M': /* a symbol with a C-style escape sequences */ { stix_ooci_t saved_c = c; GET_CHAR_TO (stix, c); if (c == '\'') { /*GET_CHAR (stix);*/ if (get_string(stix, '\'', '\\', 0, 0) <= -1) return -1; if (saved_c == 'C') { if (stix->c->tok.name.len != 1) { set_syntax_error (stix, STIX_SYNERR_CHARLIT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } SET_TOKEN_TYPE (stix, STIX_IOTOK_CHARLIT); } else if (saved_c == 'M') { SET_TOKEN_TYPE (stix, STIX_IOTOK_SYMLIT); } } else { if (get_ident(stix, saved_c) <= -1) return -1; } break; } /* case 'B': TODO: byte string with a c-style escape sequence? */ /* case 'R': TODO: regular expression? GET_CHAR_TO (stix, c); if (c == '\'') { GET_CHAR (stix); if (get_rexlit(stix) <= -1) return -1; } else { if (get_ident(stix, 'R') <= -1) return -1; } break; */ default: if (is_leadidentchar(c)) { if (get_ident(stix, STIX_UCI_EOF) <= -1) return -1; } else if (is_digitchar(c)) { if (get_numlit(stix, 0) <= -1) return -1; } else if (is_binselchar(c)) { /* binary selector */ if (get_binsel(stix) <= -1) return -1; } else { stix->c->ilchr = (stix_ooch_t)c; set_syntax_error (stix, STIX_SYNERR_ILCHR, LEXER_LOC(stix), &stix->c->ilchr_ucs); return -1; } break; single_char_token: ADD_TOKEN_CHAR(stix, c); break; } STIX_DEBUG2 (stix, "TOKEN: [%.*S]\n", (stix_ooi_t)stix->c->tok.name.len, stix->c->tok.name.ptr); return 0; } static void clear_io_names (stix_t* stix) { stix_iolink_t* cur; STIX_ASSERT (stix, stix->c != STIX_NULL); while (stix->c->io_names) { cur = stix->c->io_names; stix->c->io_names = cur->link; stix_freemem (stix, cur); } } static const stix_ooch_t* add_io_name (stix_t* stix, const stix_oocs_t* name) { stix_iolink_t* link; stix_ooch_t* ptr; link = (stix_iolink_t*) stix_callocmem (stix, STIX_SIZEOF(*link) + STIX_SIZEOF(stix_ooch_t) * (name->len + 1)); if (!link) return STIX_NULL; ptr = (stix_ooch_t*)(link + 1); stix_copyoochars (ptr, name->ptr, name->len); ptr[name->len] = '\0'; link->link = stix->c->io_names; stix->c->io_names = link; return ptr; } static int begin_include (stix_t* stix) { stix_ioarg_t* arg; const stix_ooch_t* io_name; io_name = add_io_name (stix, TOKEN_NAME(stix)); if (!io_name) return -1; arg = (stix_ioarg_t*) stix_callocmem (stix, STIX_SIZEOF(*arg)); if (!arg) goto oops; arg->name = io_name; arg->line = 1; arg->colm = 1; /*arg->nl = '\0';*/ arg->includer = stix->c->curinp; if (stix->c->impl (stix, STIX_IO_OPEN, arg) <= -1) { set_syntax_error (stix, STIX_SYNERR_INCLUDE, TOKEN_LOC(stix), TOKEN_NAME(stix)); goto oops; } GET_TOKEN (stix); if (TOKEN_TYPE(stix) != STIX_IOTOK_PERIOD) { /* check if a period is following the includee name */ set_syntax_error (stix, STIX_SYNERR_PERIOD, TOKEN_LOC(stix), TOKEN_NAME(stix)); goto oops; } /* switch to the includee's stream */ stix->c->curinp = arg; /* stix->c->depth.incl++; */ /* read in the first character in the included file. * so the next call to get_token() sees the character read * from this file. */ if (get_token(stix) <= -1) { end_include (stix); /* i don't jump to oops since i've called * end_include() which frees stix->c->curinp/arg */ return -1; } return 0; oops: if (arg) stix_freemem (stix, arg); return -1; } static int end_include (stix_t* stix) { int x; stix_ioarg_t* cur; if (stix->c->curinp == &stix->c->arg) return 0; /* no include */ /* if it is an included file, close it and * retry to read a character from an outer file */ x = stix->c->impl (stix, STIX_IO_CLOSE, stix->c->curinp); /* if closing has failed, still destroy the * sio structure first as normal and return * the failure below. this way, the caller * does not call STIX_IO_CLOSE on * stix->c->curinp again. */ cur = stix->c->curinp; stix->c->curinp = stix->c->curinp->includer; STIX_ASSERT (stix, cur->name != STIX_NULL); stix_freemem (stix, cur); /* stix->parse.depth.incl--; */ if (x != 0) { /* the failure mentioned above is returned here */ return -1; } stix->c->lxc = stix->c->curinp->lxc; return 1; /* ended the included file successfully */ } /* --------------------------------------------------------------------- * Byte-Code Generator * --------------------------------------------------------------------- */ static STIX_INLINE int emit_byte_instruction (stix_t* stix, stix_oob_t code) { /* the context object has the ip field. it should be representable * in a small integer. for simplicity, limit the total byte code length * to fit in a small integer. because 'ip' points to the next instruction * to execute, he upper bound should be (max - 1) so that i stays * at the max when incremented */ if (stix->c->mth.code.len == STIX_SMOOI_MAX - 1) { stix->errnum = STIX_EBCFULL; /* byte code too big */ return -1; } if (stix->c->mth.code.len >= stix->c->mth.code_capa) { stix_oob_t* tmp; stix_oow_t newcapa; newcapa = STIX_ALIGN (stix->c->mth.code.len + 1, CODE_BUFFER_ALIGN); tmp = stix_reallocmem (stix, stix->c->mth.code.ptr, newcapa * STIX_SIZEOF(*tmp)); if (!tmp) return -1; stix->c->mth.code.ptr = tmp; stix->c->mth.code_capa = newcapa; } stix->c->mth.code.ptr[stix->c->mth.code.len++] = code; return 0; } static int emit_single_param_instruction (stix_t* stix, int cmd, stix_oow_t param_1) { stix_oob_t bc; switch (cmd) { case BCODE_PUSH_INSTVAR_0: case BCODE_STORE_INTO_INSTVAR_0: case BCODE_POP_INTO_INSTVAR_0: case BCODE_PUSH_TEMPVAR_0: case BCODE_STORE_INTO_TEMPVAR_0: case BCODE_POP_INTO_TEMPVAR_0: case BCODE_PUSH_LITERAL_0: if (param_1 < 8) { /* low 3 bits to hold the parameter */ bc = (stix_oob_t)(cmd & 0xF8) | (stix_oob_t)param_1; goto write_short; } else { /* convert the code to a long version */ bc = cmd | 0x80; goto write_long; } case BCODE_PUSH_OBJECT_0: case BCODE_STORE_INTO_OBJECT_0: case BCODE_POP_INTO_OBJECT_0: case BCODE_JUMP_FORWARD_0: case BCODE_JUMP_BACKWARD_0: case BCODE_JUMP_IF_TRUE_0: case BCODE_JUMP_IF_FALSE_0: if (param_1 < 4) { /* low 2 bits to hold the parameter */ bc = (stix_oob_t)(cmd & 0xFC) | (stix_oob_t)param_1; goto write_short; } else { /* convert the code to a long version */ bc = cmd | 0x80; goto write_long; } case BCODE_JUMP2_FORWARD: case BCODE_JUMP2_BACKWARD: case BCODE_PUSH_INTLIT: case BCODE_PUSH_NEGINTLIT: case BCODE_PUSH_CHARLIT: bc = cmd; goto write_long; } stix->errnum = STIX_EINVAL; return -1; write_short: if (emit_byte_instruction(stix, bc) <= -1) return -1; return 0; write_long: if (param_1 > MAX_CODE_PARAM) { stix->errnum = STIX_ERANGE; return -1; } #if (STIX_BCODE_LONG_PARAM_SIZE == 2) if (emit_byte_instruction(stix, bc) <= -1 || emit_byte_instruction(stix, (param_1 >> 8) & 0xFF) <= -1 || emit_byte_instruction(stix, param_1 & 0xFF) <= -1) return -1; #else if (emit_byte_instruction(stix, bc) <= -1 || emit_byte_instruction(stix, param_1) <= -1) return -1; #endif return 0; } static int emit_double_param_instruction (stix_t* stix, int cmd, stix_oow_t param_1, stix_oow_t param_2) { stix_oob_t bc; switch (cmd) { case BCODE_STORE_INTO_CTXTEMPVAR_0: case BCODE_POP_INTO_CTXTEMPVAR_0: case BCODE_PUSH_CTXTEMPVAR_0: case BCODE_PUSH_OBJVAR_0: case BCODE_STORE_INTO_OBJVAR_0: case BCODE_POP_INTO_OBJVAR_0: case BCODE_SEND_MESSAGE_0: case BCODE_SEND_MESSAGE_TO_SUPER_0: if (param_1 < 4 && param_2 < 0xFF) { /* low 2 bits of the instruction code is the first parameter */ bc = (stix_oob_t)(cmd & 0xFC) | (stix_oob_t)param_1; goto write_short; } else { /* convert the code to a long version */ bc = cmd | 0x80; goto write_long; } case BCODE_MAKE_BLOCK: bc = cmd; goto write_long; } stix->errnum = STIX_EINVAL; return -1; write_short: if (emit_byte_instruction(stix, bc) <= -1 || emit_byte_instruction(stix, param_2) <= -1) return -1; return 0; write_long: if (param_1 > MAX_CODE_PARAM || param_2 > MAX_CODE_PARAM) { stix->errnum = STIX_ERANGE; return -1; } #if (STIX_BCODE_LONG_PARAM_SIZE == 2) if (emit_byte_instruction(stix, bc) <= -1 || emit_byte_instruction(stix, (param_1 >> 8) & 0xFF) <= -1 || emit_byte_instruction(stix, param_1 & 0xFF) <= -1 || emit_byte_instruction(stix, (param_2 >> 8) & 0xFF) <= -1 || emit_byte_instruction(stix, param_2 & 0xFF) <= -1) return -1; #else if (emit_byte_instruction(stix, bc) <= -1 || emit_byte_instruction(stix, param_1) <= -1 || emit_byte_instruction(stix, param_2) <= -1) return -1; #endif return 0; } static int emit_push_smooi_literal (stix_t* stix, stix_ooi_t i) { stix_oow_t index; switch (i) { case -1: return emit_byte_instruction (stix, BCODE_PUSH_NEGONE); case 0: return emit_byte_instruction (stix, BCODE_PUSH_ZERO); case 1: return emit_byte_instruction (stix, BCODE_PUSH_ONE); case 2: return emit_byte_instruction (stix, BCODE_PUSH_TWO); } if (i >= 0 && i <= MAX_CODE_PARAM) { return emit_single_param_instruction(stix, BCODE_PUSH_INTLIT, i); } else if (i < 0 && i >= -(stix_ooi_t)MAX_CODE_PARAM) { return emit_single_param_instruction(stix, BCODE_PUSH_NEGINTLIT, -i); } if (add_literal(stix, STIX_SMOOI_TO_OOP(i), &index) <= -1 || emit_single_param_instruction(stix, BCODE_PUSH_LITERAL_0, index) <= -1) return -1; return 0; } static int emit_push_character_literal (stix_t* stix, stix_ooch_t ch) { stix_oow_t index; if (ch >= 0 && ch <= MAX_CODE_PARAM) { return emit_single_param_instruction (stix, BCODE_PUSH_CHARLIT, ch); } if (add_literal(stix, STIX_CHAR_TO_OOP(ch), &index) <= -1 || emit_single_param_instruction(stix, BCODE_PUSH_LITERAL_0, index) <= -1) return -1; return 0; } /* --------------------------------------------------------------------- * Compiler * --------------------------------------------------------------------- */ static int add_literal (stix_t* stix, stix_oop_t lit, stix_oow_t* index) { stix_oow_t i; for (i = 0; i < stix->c->mth.literal_count; i++) { /* * this removes redundancy of symbols, characters, and small integers. * more complex redundacy check may be done somewhere else like * in add_string_literal(). */ if (stix->c->mth.literals[i] == lit) { *index = i; return i; } } if (stix->c->mth.literal_count >= stix->c->mth.literal_capa) { stix_oop_t* tmp; stix_oow_t new_capa; new_capa = STIX_ALIGN (stix->c->mth.literal_count + 1, LITERAL_BUFFER_ALIGN); tmp = (stix_oop_t*)stix_reallocmem (stix, stix->c->mth.literals, new_capa * STIX_SIZEOF(*tmp)); if (!tmp) return -1; stix->c->mth.literal_capa = new_capa; stix->c->mth.literals = tmp; } *index = stix->c->mth.literal_count; stix->c->mth.literals[stix->c->mth.literal_count++] = lit; return 0; } static int add_string_literal (stix_t* stix, const stix_oocs_t* str, stix_oow_t* index) { stix_oop_t lit; stix_oow_t i; for (i = 0; i < stix->c->mth.literal_count; i++) { lit = stix->c->mth.literals[i]; if (STIX_CLASSOF(stix, lit) == stix->_string && STIX_OBJ_GET_SIZE(lit) == str->len && stix_equaloochars(((stix_oop_char_t)lit)->slot, str->ptr, str->len)) { *index = i; return 0; } } lit = stix_instantiate (stix, stix->_string, str->ptr, str->len); if (!lit) return -1; return add_literal (stix, lit, index); } static int add_symbol_literal (stix_t* stix, const stix_oocs_t* str, int offset, stix_oow_t* index) { stix_oop_t tmp; tmp = stix_makesymbol (stix, str->ptr + offset, str->len - offset); if (!tmp) return -1; return add_literal (stix, tmp, index); } static STIX_INLINE int set_class_fqn (stix_t* stix, const stix_oocs_t* name) { if (copy_string_to (stix, name, &stix->c->cls.fqn, &stix->c->cls.fqn_capa, 0, '\0') <= -1) return -1; stix->c->cls.name = stix->c->cls.fqn; return 0; } static STIX_INLINE int set_superclass_fqn (stix_t* stix, const stix_oocs_t* name) { if (copy_string_to (stix, name, &stix->c->cls.superfqn, &stix->c->cls.superfqn_capa, 0, '\0') <= -1) return -1; stix->c->cls.supername = stix->c->cls.superfqn; return 0; } static STIX_INLINE int add_class_level_variable (stix_t* stix, var_type_t index, const stix_oocs_t* name) { int n; n = copy_string_to (stix, name, &stix->c->cls.vars[index], &stix->c->cls.vars_capa[index], 1, ' '); if (n >= 0) { stix->c->cls.var_count[index]++; /* TODO: check if it exceeds STIX_MAX_NAMED_INSTVARS, STIX_MAX_CLASSVARS, STIX_MAX_CLASSINSTVARS */ } return n; } static STIX_INLINE int add_pool_dictionary (stix_t* stix, const stix_oocs_t* name, stix_oop_set_t pooldic_oop) { if (stix->c->cls.pooldic_count >= stix->c->cls.pooldic_imp_oops_capa) { stix_oow_t new_capa; stix_oop_set_t* tmp; new_capa = STIX_ALIGN(stix->c->cls.pooldic_imp_oops_capa + 1, POOLDIC_OOP_BUFFER_ALIGN); tmp = stix_reallocmem (stix, stix->c->cls.pooldic_imp_oops, new_capa * STIX_SIZEOF(stix_oop_set_t)); if (!tmp) return -1; stix->c->cls.pooldic_imp_oops_capa = new_capa; stix->c->cls.pooldic_imp_oops = tmp; } stix->c->cls.pooldic_imp_oops[stix->c->cls.pooldic_count] = pooldic_oop; stix->c->cls.pooldic_count++; /* TODO: check if pooldic_count overflows */ return 0; } static stix_ooi_t find_class_level_variable (stix_t* stix, stix_oop_class_t self, const stix_oocs_t* name, var_info_t* var) { stix_oow_t pos; stix_oop_t super; stix_oop_char_t v; stix_oop_char_t* vv; stix_oocs_t hs; int index; if (self) { STIX_ASSERT (stix, STIX_CLASSOF(stix, self) == stix->_class); /* [NOTE] * the loop here assumes that the class has the following * fields in the order shown below: * instvars * classvars * classinstvars */ vv = &self->instvars; for (index = VAR_INSTANCE; index <= VAR_CLASSINST; index++) { v = vv[index]; hs.ptr = v->slot; hs.len = STIX_OBJ_GET_SIZE(v); if (find_word_in_string(&hs, name, &pos) >= 0) { super = self->superclass; /* 'self' may be STIX_NULL if STIX_NULL has been given for it. * the caller must take good care when interpreting the meaning of * this field */ var->cls = self; goto done; } } super = self->superclass; } else { /* the class definition is not available yet. * find the variable in the compiler's own list */ for (index = VAR_INSTANCE; index <= VAR_CLASSINST; index++) { if (find_word_in_string(&stix->c->cls.vars[index], name, &pos) >= 0) { super = stix->c->cls.super_oop; var->cls = self; goto done; } } super = stix->c->cls.super_oop; } while (super != stix->_nil) { STIX_ASSERT (stix, STIX_CLASSOF(stix, super) == stix->_class); /* [NOTE] * the loop here assumes that the class has the following * fields in the order shown below: * instvars * classvars * classinstvars */ vv = &((stix_oop_class_t)super)->instvars; for (index = VAR_INSTANCE; index <= VAR_CLASSINST; index++) { v = vv[index]; hs.ptr = v->slot; hs.len = STIX_OBJ_GET_SIZE(v); if (find_word_in_string(&hs, name, &pos) >= 0) { /* class variables reside in the class where the definition is found. * that means a class variable is found in the definition of * a superclass, the superclass is the placeholder of the * class variable. on the other hand, instance variables and * class instance variables live in the current class being * compiled as they are inherited. */ var->cls = (index == VAR_CLASS)? (stix_oop_class_t)super: self; super = ((stix_oop_class_t)super)->superclass; goto done; } } super = ((stix_oop_class_t)super)->superclass; } stix->errnum = STIX_ENOENT; return -1; done: if (super != stix->_nil) { stix_oow_t spec; /* the class being compiled has a superclass */ STIX_ASSERT (stix, STIX_CLASSOF(stix, super) == stix->_class); switch (index) { case VAR_INSTANCE: /* each class has the number of named instance variables * accumulated for inheritance. the position found in the * local variable string can be adjusted by adding the * number in the superclass */ spec = STIX_OOP_TO_SMOOI(((stix_oop_class_t)super)->spec); pos += STIX_CLASS_SPEC_NAMED_INSTVAR(spec); break; case VAR_CLASS: /* [NOTE] * no adjustment is needed. * a class object is composed of three parts. * fixed-part | classinst-variables | class-variabes * the position returned here doesn't consider * class instance variables that can be potentially * placed before the class variables. */ break; case VAR_CLASSINST: spec = STIX_OOP_TO_SMOOI(((stix_oop_class_t)super)->selfspec); pos += STIX_CLASS_SELFSPEC_CLASSINSTVAR(spec); break; } } var->type = index; var->pos = pos; return pos; } static int clone_assignee (stix_t* stix, const stix_oocs_t* name, stix_oow_t* offset) { int n; stix_oow_t old_len; old_len = stix->c->mth.assignees.len; n = copy_string_to (stix, name, &stix->c->mth.assignees, &stix->c->mth.assignees_capa, 1, '\0'); if (n <= -1) return -1; /* update the pointer to of the name. its length is the same. */ /*name->ptr = stix->c->mth.assignees.ptr + old_len;*/ *offset = old_len; return 0; } static int clone_binary_selector (stix_t* stix, const stix_oocs_t* name, stix_oow_t* offset) { int n; stix_oow_t old_len; old_len = stix->c->mth.binsels.len; n = copy_string_to (stix, name, &stix->c->mth.binsels, &stix->c->mth.binsels_capa, 1, '\0'); if (n <= -1) return -1; /* update the pointer to of the name. its length is the same. */ /*name->ptr = stix->c->mth.binsels.ptr + old_len;*/ *offset = old_len; return 0; } static int clone_keyword (stix_t* stix, const stix_oocs_t* name, stix_oow_t* offset) { int n; stix_oow_t old_len; old_len = stix->c->mth.kwsels.len; n = copy_string_to (stix, name, &stix->c->mth.kwsels, &stix->c->mth.kwsels_capa, 1, '\0'); if (n <= -1) return -1; /* update the pointer to of the name. its length is the same. */ /*name->ptr = stix->c->mth.kwsels.ptr + old_len;*/ *offset = old_len; return 0; } static int add_method_name_fragment (stix_t* stix, const stix_oocs_t* name) { /* method name fragments are concatenated without any delimiters */ return copy_string_to (stix, name, &stix->c->mth.name, &stix->c->mth.name_capa, 1, '\0'); } static int method_exists (stix_t* stix, const stix_oocs_t* name) { /* check if the current class contains a method of the given name */ #ifdef MTHDIC return stix_lookupdic (stix, stix->c->cls.mthdic_oop[stix->c->mth.type], name) != STIX_NULL; #else return stix_lookupdic (stix, stix->c->cls.self_oop->mthdic[stix->c->mth.type], name) != STIX_NULL; #endif } static int add_temporary_variable (stix_t* stix, const stix_oocs_t* name) { /* temporary variable names are added to the string with leading * space if it's not the first variable */ return copy_string_to (stix, name, &stix->c->mth.tmprs, &stix->c->mth.tmprs_capa, 1, ' '); } static STIX_INLINE int find_temporary_variable (stix_t* stix, const stix_oocs_t* name, stix_oow_t* xindex) { return find_word_in_string (&stix->c->mth.tmprs, name, xindex); } static stix_oop_set_t add_namespace (stix_t* stix, stix_oop_set_t dic, const stix_oocs_t* name) { stix_oow_t tmp_count = 0; stix_oop_t sym; stix_oop_set_t ns; stix_oop_association_t ass; stix_pushtmp (stix, (stix_oop_t*)&dic); tmp_count++; sym = stix_makesymbol (stix, name->ptr, name->len); if (!sym) goto oops; stix_pushtmp (stix, &sym); tmp_count++; ns = stix_makedic (stix, stix->_namespace, NAMESPACE_SIZE); if (!ns) goto oops; /*stix_pushtmp (stix, &ns); tmp_count++;*/ ass = stix_putatdic (stix, dic, sym, (stix_oop_t)ns); if (!ass) goto oops; stix_poptmps (stix, tmp_count); return (stix_oop_set_t)ass->value; oops: stix_poptmps (stix, tmp_count); return STIX_NULL; } static int preprocess_dotted_name (stix_t* stix, int dont_add_ns, int accept_pooldic_as_ns, const stix_oocs_t* fqn, const stix_ioloc_t* fqn_loc, stix_oocs_t* name, stix_oop_set_t* ns_oop) { const stix_ooch_t* ptr, * dot; stix_oow_t len; stix_oocs_t seg; stix_oop_set_t dic; stix_oop_association_t ass; int pooldic_gotten = 0; dic = stix->sysdic; ptr = fqn->ptr; len = fqn->len; while (1) { seg.ptr = (stix_ooch_t*)ptr; dot = stix_findoochar (ptr, len, '.'); if (dot) { if (pooldic_gotten) goto wrong_name; seg.len = dot - ptr; if (is_reserved_word(&seg)) goto wrong_name; ass = stix_lookupdic (stix, dic, &seg); if (ass) { if (STIX_CLASSOF(stix, ass->value) == stix->_namespace || (seg.ptr == fqn->ptr && ass->value == (stix_oop_t)stix->sysdic)) { /* ok */ dic = (stix_oop_set_t)ass->value; } else { if (accept_pooldic_as_ns && STIX_CLASSOF(stix, ass->value) == stix->_pool_dictionary) { /* A pool dictionary is treated as if it's a name space. * However, the pool dictionary can only act as a name space * if it's the second last segment. */ dic = (stix_oop_set_t)ass->value; pooldic_gotten = 1; } else { goto wrong_name; } } } else { stix_oop_set_t t; /* the segment does not exist. add it */ if (dont_add_ns) { /* in '#extend Planet.Earth', it's an error * if Planet doesn't exist */ goto wrong_name; } /* When definining a new class, add a missing namespace */ t = add_namespace (stix, dic, &seg); if (!t) return -1; dic = t; } } else { /* this is the last segment. it should be a class name */ seg.len = len; if (is_reserved_word(&seg)) goto wrong_name; *name = seg; *ns_oop = dic; break; } ptr = dot + 1; len -= seg.len + 1; } return 0; wrong_name: seg.len += seg.ptr - fqn->ptr; seg.ptr = fqn->ptr; set_syntax_error (stix, STIX_SYNERR_NAMESPACE, fqn_loc, &seg); return -1; } static int resolve_pooldic (stix_t* stix, int dotted, const stix_oocs_t* name) { stix_oocs_t last; /* the last segment */ stix_oop_set_t ns_oop; /* name space */ stix_oop_association_t ass; stix_oow_t i; if (dotted) { if (preprocess_dotted_name(stix, 0, 0, name, TOKEN_LOC(stix), &last, &ns_oop) <= -1) return -1; } else { last = *name; /* it falls back to the name space of the class */ ns_oop = stix->c->cls.ns_oop; } /* check if the name refers to a pool dictionary */ ass = stix_lookupdic (stix, ns_oop, &last); if (!ass || STIX_CLASSOF(stix, ass->value) != stix->_pool_dictionary) { set_syntax_error (stix, STIX_SYNERR_POOLDIC, TOKEN_LOC(stix), name); return -1; } /* check if the same dictionary pool has been declared for import */ for (i = 0; i < stix->c->cls.pooldic_count; i++) { if ((stix_oop_set_t)ass->value == stix->c->cls.pooldic_imp_oops[i]) { set_syntax_error (stix, STIX_SYNERR_POOLDICDUP, TOKEN_LOC(stix), name); return -1; } } return 0; } static int import_pool_dictionary (stix_t* stix, stix_oop_set_t ns_oop, const stix_oocs_t* tok_lastseg, const stix_oocs_t* tok_name, const stix_ioloc_t* tok_loc) { stix_oop_association_t ass; stix_oow_t i; /* check if the name refers to a pool dictionary */ ass = stix_lookupdic (stix, ns_oop, tok_lastseg); if (!ass || STIX_CLASSOF(stix, ass->value) != stix->_pool_dictionary) { set_syntax_error (stix, STIX_SYNERR_POOLDIC, tok_loc, tok_name); return -1; } /* check if the same dictionary pool has been declared for import */ for (i = 0; i < stix->c->cls.pooldic_count; i++) { if ((stix_oop_set_t)ass->value == stix->c->cls.pooldic_imp_oops[i]) { set_syntax_error (stix, STIX_SYNERR_POOLDICDUP, tok_loc, tok_name); return -1; } } if (add_pool_dictionary(stix, tok_name, (stix_oop_set_t)ass->value) <= -1) return -1; if (copy_string_to (stix, tok_name, &stix->c->cls.pooldic, &stix->c->cls.pooldic_capa, 1, ' ') <= -1) { stix->c->cls.pooldic_count--; /* roll back add_pool_dictionary() */ return -1; } return 0; } static int compile_class_level_variables (stix_t* stix) { var_type_t dcl_type = VAR_INSTANCE; if (TOKEN_TYPE(stix) == STIX_IOTOK_LPAREN) { /* process variable modifiers */ GET_TOKEN (stix); if (is_token_symbol(stix, VOCA_CLASS_S)) { /* dcl(#class) */ dcl_type = VAR_CLASS; GET_TOKEN (stix); } else if (is_token_symbol(stix, VOCA_CLASSINST_S)) { /* dcl(#classinst) */ dcl_type = VAR_CLASSINST; GET_TOKEN (stix); } else if (is_token_symbol(stix, VOCA_POOLDIC_S)) { /* dcl(#pooldic) - import a pool dictionary */ dcl_type = VAR_GLOBAL; /* this is not a real type. use for branching below */ GET_TOKEN (stix); } if (TOKEN_TYPE(stix) != STIX_IOTOK_RPAREN) { set_syntax_error (stix, STIX_SYNERR_RPAREN, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); } if (dcl_type == VAR_GLOBAL) { /* pool dictionary import declaration * #dcl(#pooldic) ... */ stix_oocs_t last; stix_oop_set_t ns_oop; do { if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT_DOTTED) { if (preprocess_dotted_name(stix, 0, 0, TOKEN_NAME(stix), TOKEN_LOC(stix), &last, &ns_oop) <= -1) return -1; } else if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT) { last = stix->c->tok.name; /* it falls back to the name space of the class */ ns_oop = stix->c->cls.ns_oop; } else break; if (import_pool_dictionary(stix, ns_oop, &last, TOKEN_NAME(stix), TOKEN_LOC(stix)) <= -1) return -1; GET_TOKEN (stix); } while (1); } else { /* variable declaration */ do { if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT) { var_info_t var; /* TODO: more check on variability conflict. if it's a indexed class, check if the superclass is fixed or index. if super is fixed and self is fixed or variable-pointer, no restriction. if super is fixed and self is variable-nonpointer, no instance varaible in the super side and in the self side. if super is variable-pointer, self must be a variable-pointer. can't be fixed either if super is variable-nonpointer, self must be a variable-nonpointer of the same type. can't be fixed either if super is variable-nonpointer, no instance variable is allowed. */ if (dcl_type == VAR_INSTANCE && (stix->c->cls.flags & CLASS_INDEXED) && (stix->c->cls.indexed_type != STIX_OBJ_TYPE_OOP)) { set_syntax_error (stix, STIX_SYNERR_VARNAMEDUP, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (find_class_level_variable(stix, STIX_NULL, TOKEN_NAME(stix), &var) >= 0 || stix_lookupdic (stix, stix->sysdic, TOKEN_NAME(stix)) || /* conflicts with a top global name */ stix_lookupdic (stix, stix->c->cls.ns_oop, TOKEN_NAME(stix))) /* conflicts with a global name in the class'es name space */ { set_syntax_error (stix, STIX_SYNERR_VARNAMEDUP, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (add_class_level_variable(stix, dcl_type, TOKEN_NAME(stix)) <= -1) return -1; } else { break; } GET_TOKEN (stix); } while (1); } if (TOKEN_TYPE(stix) != STIX_IOTOK_PERIOD) { set_syntax_error (stix, STIX_SYNERR_PERIOD, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); return 0; } static int compile_unary_method_name (stix_t* stix) { STIX_ASSERT (stix, stix->c->mth.name.len == 0); STIX_ASSERT (stix, stix->c->mth.tmpr_nargs == 0); if (add_method_name_fragment(stix, TOKEN_NAME(stix)) <= -1) return -1; GET_TOKEN (stix); if (TOKEN_TYPE(stix) == STIX_IOTOK_LPAREN) { /* this is a procedural style method */ STIX_ASSERT (stix, stix->c->mth.tmpr_nargs == 0); GET_TOKEN (stix); if (TOKEN_TYPE(stix) != STIX_IOTOK_RPAREN) { do { if (TOKEN_TYPE(stix) != STIX_IOTOK_IDENT) { /* wrong argument name. identifier is expected */ set_syntax_error (stix, STIX_SYNERR_IDENT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (find_temporary_variable(stix, TOKEN_NAME(stix), STIX_NULL) >= 0) { set_syntax_error (stix, STIX_SYNERR_ARGNAMEDUP, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (add_temporary_variable(stix, TOKEN_NAME(stix)) <= -1) return -1; stix->c->mth.tmpr_nargs++; GET_TOKEN (stix); if (TOKEN_TYPE(stix) == STIX_IOTOK_RPAREN) break; if (TOKEN_TYPE(stix) != STIX_IOTOK_COMMA) { set_syntax_error (stix, STIX_SYNERR_COMMA, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); } while (1); } /* indicate that the unary method name is followed by a parameter list */ stix->c->mth.variadic = 1; GET_TOKEN (stix); } return 0; } static int compile_binary_method_name (stix_t* stix) { STIX_ASSERT (stix, stix->c->mth.name.len == 0); STIX_ASSERT (stix, stix->c->mth.tmpr_nargs == 0); if (add_method_name_fragment(stix, TOKEN_NAME(stix)) <= -1) return -1; GET_TOKEN (stix); /* collect the argument name */ if (TOKEN_TYPE(stix) != STIX_IOTOK_IDENT) { /* wrong argument name. identifier expected */ set_syntax_error (stix, STIX_SYNERR_IDENT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } STIX_ASSERT (stix, stix->c->mth.tmpr_nargs == 0); /* no duplication check is performed against class-level variable names. * a duplcate name will shade a previsouly defined variable. */ if (add_temporary_variable(stix, TOKEN_NAME(stix)) <= -1) return -1; stix->c->mth.tmpr_nargs++; STIX_ASSERT (stix, stix->c->mth.tmpr_nargs == 1); /* this check should not be not necessary if (stix->c->mth.tmpr_nargs > MAX_CODE_NARGS) { set_syntax_error (stix, STIX_SYNERR_ARGFLOOD, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } */ GET_TOKEN (stix); return 0; } static int compile_keyword_method_name (stix_t* stix) { STIX_ASSERT (stix, stix->c->mth.name.len == 0); STIX_ASSERT (stix, stix->c->mth.tmpr_nargs == 0); do { if (add_method_name_fragment(stix, TOKEN_NAME(stix)) <= -1) return -1; GET_TOKEN (stix); if (TOKEN_TYPE(stix) != STIX_IOTOK_IDENT) { /* wrong argument name. identifier is expected */ set_syntax_error (stix, STIX_SYNERR_IDENT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (find_temporary_variable(stix, TOKEN_NAME(stix), STIX_NULL) >= 0) { set_syntax_error (stix, STIX_SYNERR_ARGNAMEDUP, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (add_temporary_variable(stix, TOKEN_NAME(stix)) <= -1) return -1; stix->c->mth.tmpr_nargs++; GET_TOKEN (stix); } while (TOKEN_TYPE(stix) == STIX_IOTOK_KEYWORD); return 0; } static int compile_method_name (stix_t* stix) { /* * method-name := unary-method-name | binary-method-name | keyword-method-name * unary-method-name := unary-selector * binary-method-name := binary-selector selector-argument * keyword-method-name := (keyword selector-argument)+ * selector-argument := identifier * unary-selector := identifier */ int n; STIX_ASSERT (stix, stix->c->mth.tmpr_count == 0); stix->c->mth.name_loc = stix->c->tok.loc; switch (TOKEN_TYPE(stix)) { case STIX_IOTOK_IDENT: n = compile_unary_method_name(stix); break; case STIX_IOTOK_BINSEL: n = compile_binary_method_name(stix); break; case STIX_IOTOK_KEYWORD: n = compile_keyword_method_name(stix); break; default: /* illegal method name */ set_syntax_error (stix, STIX_SYNERR_MTHNAME, TOKEN_LOC(stix), TOKEN_NAME(stix)); n = -1; } if (n >= 0) { if (method_exists(stix, &stix->c->mth.name)) { set_syntax_error (stix, STIX_SYNERR_MTHNAMEDUP, &stix->c->mth.name_loc, &stix->c->mth.name); return -1; } } STIX_ASSERT (stix, stix->c->mth.tmpr_nargs < MAX_CODE_NARGS); /* the total number of temporaries is equal to the number of * arguments after having processed the message pattern. it's because * stix treats arguments the same as temporaries */ stix->c->mth.tmpr_count = stix->c->mth.tmpr_nargs; return n; } static int compile_method_temporaries (stix_t* stix) { /* * method-temporaries := "|" variable-list "|" * variable-list := identifier* */ if (!is_token_binary_selector(stix, VOCA_VBAR)) { /* return without doing anything if | is not found. * this is not an error condition */ return 0; } GET_TOKEN (stix); while (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT) { if (find_temporary_variable(stix, TOKEN_NAME(stix), STIX_NULL) >= 0) { set_syntax_error (stix, STIX_SYNERR_TMPRNAMEDUP, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (add_temporary_variable(stix, TOKEN_NAME(stix)) <= -1) return -1; stix->c->mth.tmpr_count++; if (stix->c->mth.tmpr_count > MAX_CODE_NARGS) { set_syntax_error (stix, STIX_SYNERR_TMPRFLOOD, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); } if (!is_token_binary_selector(stix, VOCA_VBAR)) { set_syntax_error (stix, STIX_SYNERR_VBAR, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); return 0; } static int compile_method_primitive (stix_t* stix) { /* * method-primitive := "<" "primitive:" integer ">" | * "<" "primitive:" symbol ">" | * "<" "exception" ">" | * "<" "ensure" ">" */ stix_ooi_t pfnum; const stix_ooch_t* ptr, * end; if (!is_token_binary_selector(stix, VOCA_LT)) { /* return if < is not seen. it is not an error condition */ return 0; } GET_TOKEN (stix); if (is_token_keyword(stix, VOCA_PRIMITIVE_COLON)) { GET_TOKEN (stix); switch (TOKEN_TYPE(stix)) { case STIX_IOTOK_NUMLIT: /* TODO: allow only an integer */ /*TODO: more checks the validity of the primitive number. support number with radix and so on support more extensive syntax. support primitive name, not number*/ ptr = TOKEN_NAME_PTR(stix); end = ptr + TOKEN_NAME_LEN(stix); pfnum = 0; while (ptr < end && is_digitchar(*ptr)) { pfnum = pfnum * 10 + (*ptr - '0'); if (!STIX_OOI_IN_METHOD_PREAMBLE_INDEX_RANGE(pfnum)) { set_syntax_error (stix, STIX_SYNERR_PFNUM, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } ptr++; } stix->c->mth.pfnum = pfnum; break; case STIX_IOTOK_SYMLIT: { const stix_ooch_t* tptr; stix_oow_t tlen; tptr = TOKEN_NAME_PTR(stix) + 1; tlen = TOKEN_NAME_LEN(stix) - 1; /* attempt get a primitive function number by name */ pfnum = stix_getpfnum (stix, tptr, tlen); if (pfnum <= -1) { /* a built-in primitive function is not found * check if it is a primitive function identifier */ stix_oow_t lit_idx; if (stix_findoochar (tptr, tlen, '.') && add_symbol_literal(stix, TOKEN_NAME(stix), 1, &lit_idx) >= 0 && STIX_OOI_IN_METHOD_PREAMBLE_INDEX_RANGE(lit_idx)) { /* external named primitive containing a period. */ stix->c->mth.pftype = 2; stix->c->mth.pfnum = lit_idx; break; } /* wrong primitive number */ set_syntax_error (stix, STIX_SYNERR_PFID, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } else if (!STIX_OOI_IN_METHOD_PREAMBLE_INDEX_RANGE(pfnum)) { set_syntax_error (stix, STIX_SYNERR_PFID, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } stix->c->mth.pftype = 1; stix->c->mth.pfnum = pfnum; break; } default: set_syntax_error (stix, STIX_SYNERR_INTEGER, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } } else if (is_token_word(stix, VOCA_EXCEPTION)) { /* TODO: exception handler is supposed to be used by BlockContext on:do:. * it needs to check the number of arguments at least */ stix->c->mth.pftype = 3; } else if (is_token_word(stix, VOCA_ENSURE)) { stix->c->mth.pftype = 4; } else { set_syntax_error (stix, STIX_SYNERR_PRIMITIVE, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); if (!is_token_binary_selector(stix, VOCA_GT)) { set_syntax_error (stix, STIX_SYNERR_GT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); return 0; } static int get_variable_info (stix_t* stix, const stix_oocs_t* name, const stix_ioloc_t* name_loc, int name_dotted, var_info_t* var) { stix_oow_t index; STIX_MEMSET (var, 0, STIX_SIZEOF(*var)); if (name_dotted) { /* if a name is dotted, * * self.XXX - instance variable * A.B.C - namespace or pool dictionary related reference. */ stix_oocs_t last; stix_oop_set_t ns_oop; stix_oop_association_t ass; const stix_ooch_t* dot; dot = stix_findoochar (name->ptr, name->len, '.'); STIX_ASSERT (stix, dot != STIX_NULL); if (dot - (const stix_ooch_t*)name->ptr == 4 && stix_equaloochars(name->ptr, vocas[VOCA_SELF].str, 4)) { /* the dotted name begins with self. */ dot = stix_findoochar (dot + 1, name->len - 5, '.'); if (!dot) { /* the dotted name is composed of 2 segments only */ last.ptr = name->ptr + 5; last.len = name->len - 5; if (!is_reserved_word(&last)) { if (find_class_level_variable(stix, stix->c->cls.self_oop, &last, var) >= 0) { goto class_level_variable; } else { /* undeclared identifier */ set_syntax_error (stix, STIX_SYNERR_VARUNDCL, name_loc, name); return -1; } } } } if (preprocess_dotted_name (stix, 1, 1, name, name_loc, &last, &ns_oop) <= -1) return -1; ass = stix_lookupdic (stix, ns_oop, &last); if (ass) { var->type = VAR_GLOBAL; var->gbl = ass; } else { /* undeclared identifier */ set_syntax_error (stix, STIX_SYNERR_VARUNDCL, name_loc, name); return -1; } return 0; } if (find_temporary_variable (stix, name, &index) >= 0) { var->type = (index < stix->c->mth.tmpr_nargs)? VAR_ARGUMENT: VAR_TEMPORARY; var->pos = index; } else { if (find_class_level_variable(stix, stix->c->cls.self_oop, name, var) >= 0) { class_level_variable: switch (var->type) { case VAR_INSTANCE: if (stix->c->mth.type == STIX_METHOD_CLASS) { /* a class method cannot access an instance variable */ set_syntax_error (stix, STIX_SYNERR_VARINACC, name_loc, name); return -1; } break; case VAR_CLASS: /* a class variable can be access by both instance methods and class methods */ STIX_ASSERT (stix, var->cls != STIX_NULL); STIX_ASSERT (stix, STIX_CLASSOF(stix, var->cls) == stix->_class); /* increment the position by the number of class instance variables * as the class variables are placed after the class instance variables */ var->pos += STIX_CLASS_NAMED_INSTVARS + STIX_CLASS_SELFSPEC_CLASSINSTVAR(STIX_OOP_TO_SMOOI(var->cls->selfspec)); break; case VAR_CLASSINST: /* class instance variable can be accessed by only class methods */ if (stix->c->mth.type == STIX_METHOD_INSTANCE) { /* an instance method cannot access a class-instance variable */ set_syntax_error (stix, STIX_SYNERR_VARINACC, name_loc, name); return -1; } /* to a class object itself, a class-instance variable is * just an instance variriable. but these are located * after the named instance variables. */ var->pos += STIX_CLASS_NAMED_INSTVARS; break; default: /* internal error - it must not happen */ stix->errnum = STIX_EINTERN; return -1; } } else { stix_oop_association_t ass; /*ass = stix_lookupsysdic (stix, name);*/ ass = stix_lookupdic (stix, stix->c->cls.ns_oop, name); if (!ass && stix->c->cls.ns_oop != stix->sysdic) ass = stix_lookupdic (stix, stix->sysdic, name); if (ass) { var->type = VAR_GLOBAL; var->gbl = ass; } else { stix_oow_t i; stix_oop_association_t ass2 = STIX_NULL; /* attempt to find the variable in pool dictionaries */ for (i = 0; i < stix->c->cls.pooldic_count; i++) { ass = stix_lookupdic (stix, stix->c->cls.pooldic_imp_oops[i], name); if (ass) { if (ass2) { /* the variable name has been found at least in 2 dictionaries */ set_syntax_error (stix, STIX_SYNERR_VARAMBIG, name_loc, name); return -1; } ass2 = ass; } } if (ass2) { var->type = VAR_GLOBAL; var->gbl = ass2; } else { /* undeclared identifier */ set_syntax_error (stix, STIX_SYNERR_VARUNDCL, name_loc, name); return -1; } } } } if (var->pos > MAX_CODE_INDEX) { /* the assignee is not usable because its index is too large * to be expressed in byte-codes. */ set_syntax_error (stix, STIX_SYNERR_VARUNUSE, name_loc, name); return -1; } return 0; } static int compile_block_temporaries (stix_t* stix) { /* * block-temporaries := "|" variable-list "|" * variable-list := identifier* */ if (!is_token_binary_selector(stix, VOCA_VBAR)) { /* return without doing anything if | is not found. * this is not an error condition */ return 0; } GET_TOKEN (stix); while (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT) { if (find_temporary_variable(stix, TOKEN_NAME(stix), STIX_NULL) >= 0) { set_syntax_error (stix, STIX_SYNERR_TMPRNAMEDUP, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (add_temporary_variable(stix, TOKEN_NAME(stix)) <= -1) return -1; stix->c->mth.tmpr_count++; if (stix->c->mth.tmpr_count > MAX_CODE_NTMPRS) { set_syntax_error (stix, STIX_SYNERR_TMPRFLOOD, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); } if (!is_token_binary_selector(stix, VOCA_VBAR)) { set_syntax_error (stix, STIX_SYNERR_VBAR, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); return 0; } static int store_tmpr_count_for_block (stix_t* stix, stix_oow_t tmpr_count) { if (stix->c->mth.blk_depth >= stix->c->mth.blk_tmprcnt_capa) { stix_oow_t* tmp; stix_oow_t new_capa; new_capa = STIX_ALIGN (stix->c->mth.blk_depth + 1, BLK_TMPRCNT_BUFFER_ALIGN); tmp = (stix_oow_t*)stix_reallocmem (stix, stix->c->mth.blk_tmprcnt, new_capa * STIX_SIZEOF(*tmp)); if (!tmp) return -1; stix->c->mth.blk_tmprcnt_capa = new_capa; stix->c->mth.blk_tmprcnt = tmp; } /* [NOTE] i don't increment blk_depth here. it's updated * by the caller after this function has been called for * a new block entered. */ stix->c->mth.blk_tmprcnt[stix->c->mth.blk_depth] = tmpr_count; return 0; } static int compile_block_expression (stix_t* stix) { stix_oow_t jump_inst_pos; stix_oow_t saved_tmpr_count, saved_tmprs_len; stix_oow_t block_arg_count, block_tmpr_count; stix_oow_t block_code_size; stix_ioloc_t block_loc, colon_loc, tmpr_loc; /* * block-expression := "[" block-body "]" * block-body := (block-argument* "|")? block-temporaries? method-statement* * block-argument := ":" identifier */ /* this function expects [ not to be consumed away */ STIX_ASSERT (stix, TOKEN_TYPE(stix) == STIX_IOTOK_LBRACK); block_loc = stix->c->tok.loc; GET_TOKEN (stix); saved_tmprs_len = stix->c->mth.tmprs.len; saved_tmpr_count = stix->c->mth.tmpr_count; STIX_ASSERT (stix, stix->c->mth.blk_depth > 0); STIX_ASSERT (stix, stix->c->mth.blk_tmprcnt[stix->c->mth.blk_depth - 1] == saved_tmpr_count); if (TOKEN_TYPE(stix) == STIX_IOTOK_COLON) { colon_loc = stix->c->tok.loc; /* block temporary variables */ do { GET_TOKEN (stix); if (TOKEN_TYPE(stix) != STIX_IOTOK_IDENT) { /* wrong argument name. identifier expected */ set_syntax_error (stix, STIX_SYNERR_IDENT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } /* TODO: check conflicting names as well */ if (find_temporary_variable(stix, TOKEN_NAME(stix), STIX_NULL) >= 0) { set_syntax_error (stix, STIX_SYNERR_BLKARGNAMEDUP, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (add_temporary_variable(stix, TOKEN_NAME(stix)) <= -1) return -1; stix->c->mth.tmpr_count++; if (stix->c->mth.tmpr_count > MAX_CODE_NARGS) { set_syntax_error (stix, STIX_SYNERR_BLKARGFLOOD, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); } while (TOKEN_TYPE(stix) == STIX_IOTOK_COLON); if (!is_token_binary_selector(stix, VOCA_VBAR)) { set_syntax_error (stix, STIX_SYNERR_VBAR, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); } block_arg_count = stix->c->mth.tmpr_count - saved_tmpr_count; if (block_arg_count > MAX_CODE_NBLKARGS) { /* while an integer object is pused to indicate the number of * block arguments, evaluation which is done by message passing * limits the number of arguments that can be passed. so the * check is implemented */ set_syntax_error (stix, STIX_SYNERR_BLKARGFLOOD, &colon_loc, STIX_NULL); return -1; } tmpr_loc = stix->c->tok.loc; if (compile_block_temporaries(stix) <= -1) return -1; /* this is a block-local temporary count including arguments */ block_tmpr_count = stix->c->mth.tmpr_count - saved_tmpr_count; if (block_tmpr_count > MAX_CODE_NBLKTMPRS) { set_syntax_error (stix, STIX_SYNERR_BLKTMPRFLOOD, &tmpr_loc, STIX_NULL); return -1; } /* store the accumulated number of temporaries for the current block. * block depth is not raised as it's not entering a new block but * updating the temporaries count for the current block. */ if (store_tmpr_count_for_block (stix, stix->c->mth.tmpr_count) <= -1) return -1; #if defined(STIX_USE_MAKE_BLOCK) if (emit_double_param_instruction(stix, BCODE_MAKE_BLOCK, block_arg_count, stix->c->mth.tmpr_count/*block_tmpr_count*/) <= -1) return -1; #else if (emit_byte_instruction(stix, BCODE_PUSH_CONTEXT) <= -1 || emit_push_smooi_literal(stix, block_arg_count) <= -1 || emit_push_smooi_literal(stix, stix->c->mth.tmpr_count/*block_tmpr_count*/) <= -1 || emit_byte_instruction(stix, BCODE_SEND_BLOCK_COPY) <= -1) return -1; #endif /* insert dummy instructions before replacing them with a jump instruction */ jump_inst_pos = stix->c->mth.code.len; /* specifying MAX_CODE_JUMP causes emit_single_param_instruction() to * produce the long jump instruction (BCODE_JUMP_FORWARD_X) */ if (emit_single_param_instruction (stix, BCODE_JUMP_FORWARD_0, MAX_CODE_JUMP) <= -1) return -1; /* compile statements inside a block */ if (TOKEN_TYPE(stix) == STIX_IOTOK_RBRACK) { /* the block is empty */ if (emit_byte_instruction (stix, BCODE_PUSH_NIL) <= -1) return -1; } else { while (TOKEN_TYPE(stix) != STIX_IOTOK_EOF) { if (compile_block_statement(stix) <= -1) return -1; if (TOKEN_TYPE(stix) == STIX_IOTOK_RBRACK) break; else if (TOKEN_TYPE(stix) == STIX_IOTOK_PERIOD) { GET_TOKEN (stix); if (TOKEN_TYPE(stix) == STIX_IOTOK_RBRACK) break; if (emit_byte_instruction(stix, BCODE_POP_STACKTOP) <= -1) return -1; } else { set_syntax_error (stix, STIX_SYNERR_RBRACK, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } } } if (emit_byte_instruction(stix, BCODE_RETURN_FROM_BLOCK) <= -1) return -1; /* STIX_BCODE_LONG_PARAM_SIZE + 1 => size of the long JUMP_FORWARD instruction */ block_code_size = stix->c->mth.code.len - jump_inst_pos - (STIX_BCODE_LONG_PARAM_SIZE + 1); if (block_code_size > MAX_CODE_JUMP * 2) { set_syntax_error (stix, STIX_SYNERR_BLKFLOOD, &block_loc, STIX_NULL); return -1; } else { stix_oow_t jump_offset; if (block_code_size > MAX_CODE_JUMP) { /* switch to JUMP2 instruction to allow a bigger jump offset. * up to twice MAX_CODE_JUMP only */ stix->c->mth.code.ptr[jump_inst_pos] = BCODE_JUMP2_FORWARD; jump_offset = block_code_size - MAX_CODE_JUMP; } else { jump_offset = block_code_size; } #if (STIX_BCODE_LONG_PARAM_SIZE == 2) stix->c->mth.code.ptr[jump_inst_pos + 1] = jump_offset >> 8; stix->c->mth.code.ptr[jump_inst_pos + 2] = jump_offset & 0xFF; #else stix->c->mth.code.ptr[jump_inst_pos + 1] = jump_offset; #endif } /* restore the temporary count */ stix->c->mth.tmprs.len = saved_tmprs_len; stix->c->mth.tmpr_count = saved_tmpr_count; GET_TOKEN (stix); return 0; } static int add_to_byte_array_literal_buffer (stix_t* stix, stix_oob_t b) { if (stix->c->mth.balit_count >= stix->c->mth.balit_capa) { stix_oob_t* tmp; stix_oow_t new_capa; new_capa = STIX_ALIGN (stix->c->mth.balit_count + 1, BALIT_BUFFER_ALIGN); tmp = (stix_oob_t*)stix_reallocmem (stix, stix->c->mth.balit, new_capa * STIX_SIZEOF(*tmp)); if (!tmp) return -1; stix->c->mth.balit_capa = new_capa; stix->c->mth.balit = tmp; } /* TODO: overflow check of stix->c->mth.balit_count itself */ stix->c->mth.balit[stix->c->mth.balit_count++] = b; return 0; } static int add_to_array_literal_buffer (stix_t* stix, stix_oop_t item) { if (stix->c->mth.arlit_count >= stix->c->mth.arlit_capa) { stix_oop_t* tmp; stix_oow_t new_capa; new_capa = STIX_ALIGN (stix->c->mth.arlit_count + 1, ARLIT_BUFFER_ALIGN); tmp = (stix_oop_t*)stix_reallocmem (stix, stix->c->mth.arlit, new_capa * STIX_SIZEOF(*tmp)); if (!tmp) return -1; stix->c->mth.arlit_capa = new_capa; stix->c->mth.arlit = tmp; } /* TODO: overflow check of stix->c->mth.arlit_count itself */ stix->c->mth.arlit[stix->c->mth.arlit_count++] = item; return 0; } static int __read_byte_array_literal (stix_t* stix, stix_oop_t* xlit) { stix_ooi_t tmp; stix_oop_t ba; stix->c->mth.balit_count = 0; while (TOKEN_TYPE(stix) == STIX_IOTOK_NUMLIT || TOKEN_TYPE(stix) == STIX_IOTOK_RADNUMLIT) { /* TODO: check if the number is an integer */ if (string_to_smooi(stix, TOKEN_NAME(stix), TOKEN_TYPE(stix) == STIX_IOTOK_RADNUMLIT, &tmp) <= -1) { /* the token reader reads a valid token. no other errors * than the range error must not occur */ STIX_ASSERT (stix, stix->errnum == STIX_ERANGE); /* if the token is out of the SMOOI range, it's too big or * to small to be a byte */ set_syntax_error (stix, STIX_SYNERR_BYTERANGE, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } else if (tmp < 0 || tmp > 255) { set_syntax_error (stix, STIX_SYNERR_BYTERANGE, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (add_to_byte_array_literal_buffer(stix, tmp) <= -1) return -1; GET_TOKEN (stix); } if (TOKEN_TYPE(stix) != STIX_IOTOK_RBRACK) { set_syntax_error (stix, STIX_SYNERR_RBRACK, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } ba = stix_instantiate (stix, stix->_byte_array, stix->c->mth.balit, stix->c->mth.balit_count); if (!ba) return -1; *xlit = ba; return 0; } struct arlit_info_t { stix_oow_t pos; stix_oow_t len; }; typedef struct arlit_info_t arlit_info_t; static int __read_array_literal (stix_t* stix, stix_oop_t* xlit) { stix_oop_t lit, a; stix_oow_t i, saved_arlit_count; arlit_info_t info; info.pos = stix->c->mth.arlit_count; info.len = 0; do { switch (TOKEN_TYPE(stix)) { /* TODO: floating pointer number */ case STIX_IOTOK_NUMLIT: case STIX_IOTOK_RADNUMLIT: lit = string_to_num (stix, TOKEN_NAME(stix), TOKEN_TYPE(stix) == STIX_IOTOK_RADNUMLIT); break; case STIX_IOTOK_CHARLIT: STIX_ASSERT (stix, TOKEN_NAME_LEN(stix) == 1); lit = STIX_CHAR_TO_OOP(TOKEN_NAME_PTR(stix)[0]); break; case STIX_IOTOK_STRLIT: lit = stix_instantiate (stix, stix->_string, TOKEN_NAME_PTR(stix), TOKEN_NAME_LEN(stix)); break; case STIX_IOTOK_SYMLIT: lit = stix_makesymbol (stix, TOKEN_NAME_PTR(stix) + 1, TOKEN_NAME_LEN(stix) - 1); break; case STIX_IOTOK_IDENT: case STIX_IOTOK_IDENT_DOTTED: case STIX_IOTOK_BINSEL: case STIX_IOTOK_KEYWORD: case STIX_IOTOK_SELF: case STIX_IOTOK_SUPER: case STIX_IOTOK_THIS_CONTEXT: case STIX_IOTOK_THIS_PROCESS: lit = stix_makesymbol (stix, TOKEN_NAME_PTR(stix), TOKEN_NAME_LEN(stix)); break; case STIX_IOTOK_NIL: lit = stix->_nil; break; case STIX_IOTOK_TRUE: lit = stix->_true; break; case STIX_IOTOK_FALSE: lit = stix->_false; break; case STIX_IOTOK_ERROR: lit = STIX_ERROR_TO_OOP(STIX_EGENERIC); break; case STIX_IOTOK_ERRLIT: lit = string_to_error (stix, TOKEN_NAME(stix)); break; case STIX_IOTOK_ARPAREN: /* #( */ case STIX_IOTOK_LPAREN: /* ( */ saved_arlit_count = stix->c->mth.arlit_count; /* TODO: get rid of recursion?? */ GET_TOKEN (stix); if (__read_array_literal (stix, &lit) <= -1) return -1; stix->c->mth.arlit_count = saved_arlit_count; break; case STIX_IOTOK_BAPAREN: /* #[ */ case STIX_IOTOK_LBRACK: /* [ */ GET_TOKEN (stix); if (__read_byte_array_literal (stix, &lit) <= -1) return -1; break; default: goto done; } if (!lit || add_to_array_literal_buffer(stix, lit) <= -1) return -1; info.len++; GET_TOKEN (stix); } while (1); done: if (TOKEN_TYPE(stix) != STIX_IOTOK_RPAREN) { set_syntax_error (stix, STIX_SYNERR_RPAREN, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } a = stix_instantiate (stix, stix->_array, STIX_NULL, info.len); if (!a) return -1; for (i = 0; i < info.len; i++) { ((stix_oop_oop_t)a)->slot[i] = stix->c->mth.arlit[info.pos + i]; } *xlit = a; return 0; } static STIX_INLINE int read_byte_array_literal (stix_t* stix, stix_oop_t* xlit) { GET_TOKEN (stix); /* skip #[ and read the next token */ return __read_byte_array_literal(stix, xlit); } static int compile_byte_array_literal (stix_t* stix) { stix_oop_t lit; stix_oow_t index; if (read_byte_array_literal(stix, &lit) <= -1 || add_literal(stix, lit, &index) <= -1 || emit_single_param_instruction(stix, BCODE_PUSH_LITERAL_0, index) <= -1) return -1; GET_TOKEN (stix); return 0; } static int read_array_literal (stix_t* stix, stix_oop_t* xlit) { int x; stix_oow_t saved_arlit_count; stix->c->in_array = 1; if (get_token(stix) <= -1) { /* skip #( and read the next token */ stix->c->in_array = 0; return -1; } saved_arlit_count = stix->c->mth.arlit_count; x = __read_array_literal (stix, xlit); stix->c->mth.arlit_count = saved_arlit_count; stix->c->in_array = 0; return x; } static int compile_array_literal (stix_t* stix) { stix_oop_t lit; stix_oow_t index; STIX_ASSERT (stix, stix->c->mth.arlit_count == 0); if (read_array_literal(stix, &lit) <= -1 || add_literal(stix, lit, &index) <= -1 || emit_single_param_instruction(stix, BCODE_PUSH_LITERAL_0, index) <= -1) return -1; GET_TOKEN (stix); return 0; } static int compile_expression_primary (stix_t* stix, const stix_oocs_t* ident, const stix_ioloc_t* ident_loc, int ident_dotted, int* to_super) { /* * expression-primary := identifier | literal | block-constructor | ( "(" method-expression ")" ) */ var_info_t var; int read_next_token = 0; stix_oow_t index; *to_super = 0; if (ident) { /* the caller has read the identifier and the next word */ handle_ident: if (get_variable_info(stix, ident, ident_loc, ident_dotted, &var) <= -1) return -1; switch (var.type) { case VAR_ARGUMENT: case VAR_TEMPORARY: { #if defined(STIX_USE_CTXTEMPVAR) if (stix->c->mth.blk_depth > 0) { stix_oow_t i; /* if a temporary variable is accessed inside a block, * use a special instruction to indicate it */ STIX_ASSERT (stix, var.pos < stix->c->mth.blk_tmprcnt[stix->c->mth.blk_depth]); for (i = stix->c->mth.blk_depth; i > 0; i--) { if (var.pos >= stix->c->mth.blk_tmprcnt[i - 1]) { if (emit_double_param_instruction(stix, BCODE_PUSH_CTXTEMPVAR_0, stix->c->mth.blk_depth - i, var.pos - stix->c->mth.blk_tmprcnt[i - 1]) <= -1) return -1; goto temporary_done; } } } #endif if (emit_single_param_instruction(stix, BCODE_PUSH_TEMPVAR_0, var.pos) <= -1) return -1; temporary_done: break; } case VAR_INSTANCE: case VAR_CLASSINST: if (emit_single_param_instruction(stix, BCODE_PUSH_INSTVAR_0, var.pos) <= -1) return -1; break; case VAR_CLASS: if (add_literal(stix, (stix_oop_t)var.cls, &index) <= -1 || emit_double_param_instruction(stix, BCODE_PUSH_OBJVAR_0, var.pos, index) <= -1) return -1; break; case VAR_GLOBAL: /* [NOTE] * the association object pointed to by a system dictionary * is stored into the literal frame. so the system dictionary * must not migrate the value of the association to a new * association when it rehashes the entire dictionary. * If the association entry is deleted from the dictionary, * the code compiled before the deletion will still access * the deleted association */ if (add_literal(stix, (stix_oop_t)var.gbl, &index) <= -1 || emit_single_param_instruction(stix, BCODE_PUSH_OBJECT_0, index) <= -1) return -1; break; default: stix->errnum = STIX_EINTERN; return -1; } if (read_next_token) GET_TOKEN (stix); } else { switch (TOKEN_TYPE(stix)) { case STIX_IOTOK_IDENT_DOTTED: ident_dotted = 1; case STIX_IOTOK_IDENT: ident = TOKEN_NAME(stix); ident_loc = TOKEN_LOC(stix); read_next_token = 1; goto handle_ident; case STIX_IOTOK_SELF: if (emit_byte_instruction(stix, BCODE_PUSH_RECEIVER) <= -1) return -1; GET_TOKEN (stix); break; case STIX_IOTOK_SUPER: if (emit_byte_instruction(stix, BCODE_PUSH_RECEIVER) <= -1) return -1; GET_TOKEN (stix); *to_super = 1; break; case STIX_IOTOK_NIL: if (emit_byte_instruction(stix, BCODE_PUSH_NIL) <= -1) return -1; GET_TOKEN (stix); break; case STIX_IOTOK_TRUE: if (emit_byte_instruction(stix, BCODE_PUSH_TRUE) <= -1) return -1; GET_TOKEN (stix); break; case STIX_IOTOK_FALSE: if (emit_byte_instruction(stix, BCODE_PUSH_FALSE) <= -1) return -1; GET_TOKEN (stix); break; case STIX_IOTOK_ERROR: if (add_literal(stix, STIX_ERROR_TO_OOP(STIX_EGENERIC), &index) <= -1 || emit_single_param_instruction(stix, BCODE_PUSH_LITERAL_0, index) <= -1) return -1; GET_TOKEN (stix); break; case STIX_IOTOK_ERRLIT: { stix_oop_t tmp; tmp = string_to_error (stix, TOKEN_NAME(stix)); if (!tmp) return -1; if (add_literal(stix, tmp, &index) <= -1 || emit_single_param_instruction(stix, BCODE_PUSH_LITERAL_0, index) <= -1) return -1; GET_TOKEN (stix); break; } case STIX_IOTOK_THIS_CONTEXT: if (emit_byte_instruction(stix, BCODE_PUSH_CONTEXT) <= -1) return -1; GET_TOKEN (stix); break; case STIX_IOTOK_THIS_PROCESS: if (emit_byte_instruction(stix, BCODE_PUSH_PROCESS) <= -1) return -1; GET_TOKEN (stix); break; case STIX_IOTOK_CHARLIT: STIX_ASSERT (stix, TOKEN_NAME_LEN(stix) == 1); if (emit_push_character_literal(stix, TOKEN_NAME_PTR(stix)[0]) <= -1) return -1; GET_TOKEN (stix); break; case STIX_IOTOK_STRLIT: if (add_string_literal(stix, TOKEN_NAME(stix), &index) <= -1 || emit_single_param_instruction(stix, BCODE_PUSH_LITERAL_0, index) <= -1) return -1; GET_TOKEN (stix); break; case STIX_IOTOK_SYMLIT: if (add_symbol_literal(stix, TOKEN_NAME(stix), 1, &index) <= -1 || emit_single_param_instruction(stix, BCODE_PUSH_LITERAL_0, index) <= -1) return -1; GET_TOKEN (stix); break; case STIX_IOTOK_NUMLIT: case STIX_IOTOK_RADNUMLIT: { /* TODO: floating pointer number */ /* TODO: other types of numbers, etc */ stix_oop_t tmp; tmp = string_to_num (stix, TOKEN_NAME(stix), TOKEN_TYPE(stix) == STIX_IOTOK_RADNUMLIT); if (!tmp) return -1; if (STIX_OOP_IS_SMOOI(tmp)) { if (emit_push_smooi_literal(stix, STIX_OOP_TO_SMOOI(tmp)) <= -1) return -1; } else { if (add_literal(stix, tmp, &index) <= -1 || emit_single_param_instruction(stix, BCODE_PUSH_LITERAL_0, index) <= -1) return -1; } GET_TOKEN (stix); break; } case STIX_IOTOK_BAPAREN: /* #[ */ /*GET_TOKEN (stix);*/ if (compile_byte_array_literal(stix) <= -1) return -1; break; case STIX_IOTOK_ARPAREN: /* #( */ /*GET_TOKEN (stix);*/ if (compile_array_literal(stix) <= -1) return -1; break; /* TODO: dynamic array, non constant array #<> or #{} or what is a better bracket? */ case STIX_IOTOK_LBRACK: /* [ */ { int n; /*GET_TOKEN (stix);*/ if (store_tmpr_count_for_block (stix, stix->c->mth.tmpr_count) <= -1) return -1; stix->c->mth.blk_depth++; /* * stix->c->mth.tmpr_count[0] contains the number of temporaries for a method. * stix->c->mth.tmpr_count[1] contains the number of temporaries for the block plus the containing method. * ... * stix->c->mth.tmpr_count[n] contains the number of temporaries for the block plus all containing method and blocks. */ n = compile_block_expression(stix); stix->c->mth.blk_depth--; if (n <= -1) return -1; break; } case STIX_IOTOK_LPAREN: GET_TOKEN (stix); if (compile_method_expression(stix, 0) <= -1) return -1; if (TOKEN_TYPE(stix) != STIX_IOTOK_RPAREN) { set_syntax_error (stix, STIX_SYNERR_RPAREN, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); break; default: set_syntax_error (stix, STIX_SYNERR_PRIMARY, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } } return 0; } static stix_oob_t send_message_cmd[] = { BCODE_SEND_MESSAGE_0, BCODE_SEND_MESSAGE_TO_SUPER_0 }; static int compile_unary_message (stix_t* stix, int to_super) { stix_oow_t index; stix_oow_t nargs; STIX_ASSERT (stix, TOKEN_TYPE(stix) == STIX_IOTOK_IDENT); do { nargs = 0; if (add_symbol_literal(stix, TOKEN_NAME(stix), 0, &index) <= -1) return -1; GET_TOKEN (stix); if (TOKEN_TYPE(stix) == STIX_IOTOK_LPAREN) { /* parameterized procedure call */ GET_TOKEN(stix); if (TOKEN_TYPE(stix) != STIX_IOTOK_RPAREN) { do { if (compile_method_expression (stix, 0) <= -1) return -1; nargs++; if (TOKEN_TYPE(stix) == STIX_IOTOK_RPAREN) break; if (TOKEN_TYPE(stix) != STIX_IOTOK_COMMA) { set_syntax_error (stix, STIX_SYNERR_COMMA, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN(stix); } while (1); } GET_TOKEN(stix); /* NOTE: since the actual method may not be known at the compile time, * i can't check if nargs will match the number of arguments * expected by the method */ } if (emit_double_param_instruction(stix, send_message_cmd[to_super], nargs, index) <= -1) return -1; } while (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT); return 0; } static int compile_binary_message (stix_t* stix, int to_super) { /* * binary-message := binary-selector binary-argument * binary-argument := expression-primary unary-message* */ stix_oow_t index; int to_super2; stix_oocs_t binsel; stix_oow_t saved_binsels_len, binsel_offset; STIX_ASSERT (stix, TOKEN_TYPE(stix) == STIX_IOTOK_BINSEL); do { binsel = stix->c->tok.name; saved_binsels_len = stix->c->mth.binsels.len; if (clone_binary_selector(stix, &binsel, &binsel_offset) <= -1) goto oops; GET_TOKEN (stix); if (compile_expression_primary(stix, STIX_NULL, STIX_NULL, 0, &to_super2) <= -1) goto oops; if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT && compile_unary_message(stix, to_super2) <= -1) goto oops; /* update the pointer to the cloned selector now * to be free from reallocation risk for the recursive call * to compile_expression_primary(). */ binsel.ptr = &stix->c->mth.binsels.ptr[binsel_offset]; if (add_symbol_literal(stix, &binsel, 0, &index) <= -1 || emit_double_param_instruction(stix, send_message_cmd[to_super], 1, index) <= -1) goto oops; stix->c->mth.binsels.len = saved_binsels_len; } while (TOKEN_TYPE(stix) == STIX_IOTOK_BINSEL); return 0; oops: stix->c->mth.binsels.len = saved_binsels_len; return -1; } static int compile_keyword_message (stix_t* stix, int to_super) { /* * keyword-message := (keyword keyword-argument)+ * keyword-argument := expression-primary unary-message* binary-message* */ stix_oow_t index; int to_super2; stix_oocs_t kw, kwsel; stix_ioloc_t saved_kwsel_loc; stix_oow_t saved_kwsel_len; stix_oow_t kw_offset; stix_oow_t nargs = 0; saved_kwsel_loc = stix->c->tok.loc; saved_kwsel_len = stix->c->mth.kwsels.len; /* TODO: optimization for ifTrue: ifFalse: whileTrue: whileFalse .. */ do { kw = stix->c->tok.name; if (clone_keyword(stix, &kw, &kw_offset) <= -1) goto oops; GET_TOKEN (stix); if (compile_expression_primary(stix, STIX_NULL, STIX_NULL, 0, &to_super2) <= -1) goto oops; if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT && compile_unary_message(stix, to_super2) <= -1) goto oops; if (TOKEN_TYPE(stix) == STIX_IOTOK_BINSEL && compile_binary_message(stix, to_super2) <= -1) goto oops; kw.ptr = &stix->c->mth.kwsels.ptr[kw_offset]; if (nargs >= MAX_CODE_NARGS) { /* 'kw' points to only one segment of the full keyword message. * if it parses an expression like 'aBlock value: 10 with: 20', * 'kw' may point to 'value:' or 'with:'. */ set_syntax_error (stix, STIX_SYNERR_ARGFLOOD, &saved_kwsel_loc, &kw); goto oops; } nargs++; } while (TOKEN_TYPE(stix) == STIX_IOTOK_KEYWORD); kwsel.ptr = &stix->c->mth.kwsels.ptr[saved_kwsel_len]; kwsel.len = stix->c->mth.kwsels.len - saved_kwsel_len; if (add_symbol_literal(stix, &kwsel, 0, &index) <= -1 || emit_double_param_instruction(stix, send_message_cmd[to_super], nargs, index) <= -1) goto oops; stix->c->mth.kwsels.len = saved_kwsel_len; return 0; oops: stix->c->mth.kwsels.len = saved_kwsel_len; return -1; } static int compile_message_expression (stix_t* stix, int to_super) { /* * message-expression := single-message cascaded-message * single-message := * (unary-message+ binary-message* keyword-message?) | * (binary-message+ keyword-message?) | * keyword-message * * keyword-message := (keyword keyword-argument)+ * keyword-argument := expression-primary unary-message* binary-message* * binary-message := binary-selector binary-argument * binary-argument := expression-primary unary-message* * unary-message := unary-selector * cascaded-message := (";" single-message)* */ stix_oow_t noop_pos; do { switch (TOKEN_TYPE(stix)) { case STIX_IOTOK_IDENT: /* insert NOOP to change to DUP_STACKTOP if there is a * cascaded message */ noop_pos = stix->c->mth.code.len; if (emit_byte_instruction(stix, BCODE_NOOP) <= -1) return -1; if (compile_unary_message(stix, to_super) <= -1) return -1; if (TOKEN_TYPE(stix) == STIX_IOTOK_BINSEL) { STIX_ASSERT (stix, stix->c->mth.code.len > noop_pos); STIX_MEMMOVE (&stix->c->mth.code.ptr[noop_pos], &stix->c->mth.code.ptr[noop_pos + 1], stix->c->mth.code.len - noop_pos - 1); stix->c->mth.code.len--; noop_pos = stix->c->mth.code.len; if (emit_byte_instruction(stix, BCODE_NOOP) <= -1) return -1; if (compile_binary_message(stix, to_super) <= -1) return -1; } if (TOKEN_TYPE(stix) == STIX_IOTOK_KEYWORD) { STIX_ASSERT (stix, stix->c->mth.code.len > noop_pos); STIX_MEMMOVE (&stix->c->mth.code.ptr[noop_pos], &stix->c->mth.code.ptr[noop_pos + 1], stix->c->mth.code.len - noop_pos - 1); stix->c->mth.code.len--; noop_pos = stix->c->mth.code.len; if (emit_byte_instruction(stix, BCODE_NOOP) <= -1) return -1; if (compile_keyword_message(stix, to_super) <= -1) return -1; } break; case STIX_IOTOK_BINSEL: noop_pos = stix->c->mth.code.len; if (emit_byte_instruction(stix, BCODE_NOOP) <= -1) return -1; if (compile_binary_message(stix, to_super) <= -1) return -1; if (TOKEN_TYPE(stix) == STIX_IOTOK_KEYWORD) { STIX_ASSERT (stix, stix->c->mth.code.len > noop_pos); STIX_MEMMOVE (&stix->c->mth.code.ptr[noop_pos], &stix->c->mth.code.ptr[noop_pos + 1], stix->c->mth.code.len - noop_pos - 1); stix->c->mth.code.len--; noop_pos = stix->c->mth.code.len; if (emit_byte_instruction(stix, BCODE_NOOP) <= -1) return -1; if (compile_keyword_message(stix, to_super) <= -1) return -1; } break; case STIX_IOTOK_KEYWORD: noop_pos = stix->c->mth.code.len; if (emit_byte_instruction(stix, BCODE_NOOP) <= -1) return -1; if (compile_keyword_message(stix, to_super) <= -1) return -1; break; default: goto done; } if (TOKEN_TYPE(stix) == STIX_IOTOK_SEMICOLON) { stix->c->mth.code.ptr[noop_pos] = BCODE_DUP_STACKTOP; if (emit_byte_instruction(stix, BCODE_POP_STACKTOP) <= -1) return -1; GET_TOKEN(stix); } else { /* delete the NOOP instruction inserted */ STIX_ASSERT (stix, stix->c->mth.code.len > noop_pos); STIX_MEMMOVE (&stix->c->mth.code.ptr[noop_pos], &stix->c->mth.code.ptr[noop_pos + 1], stix->c->mth.code.len - noop_pos - 1); stix->c->mth.code.len--; goto done; } } while (1); done: return 0; } static int compile_basic_expression (stix_t* stix, const stix_oocs_t* ident, const stix_ioloc_t* ident_loc, int ident_dotted) { /* * basic-expression := expression-primary message-expression? */ int to_super; if (compile_expression_primary(stix, ident, ident_loc, ident_dotted, &to_super) <= -1) return -1; #if 0 if (TOKEN_TYPE(stix) != STIX_IOTOK_EOF && TOKEN_TYPE(stix) != STIX_IOTOK_RBRACE && TOKEN_TYPE(stix) != STIX_IOTOK_PERIOD && TOKEN_TYPE(stix) != STIX_IOTOK_SEMICOLON) { if (compile_message_expression(stix, to_super) <= -1) return -1; } #else if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT || TOKEN_TYPE(stix) == STIX_IOTOK_BINSEL || TOKEN_TYPE(stix) == STIX_IOTOK_KEYWORD) { if (compile_message_expression(stix, to_super) <= -1) return -1; } #endif return 0; } static int compile_method_expression (stix_t* stix, int pop) { /* * method-expression := method-assignment-expression | basic-expression * method-assignment-expression := identifier ":=" method-expression */ stix_oocs_t assignee; stix_oow_t index; int ret = 0; STIX_ASSERT (stix, pop == 0 || pop == 1); STIX_MEMSET (&assignee, 0, STIX_SIZEOF(assignee)); if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT || TOKEN_TYPE(stix) == STIX_IOTOK_IDENT_DOTTED) { stix_ioloc_t assignee_loc; stix_oow_t assignee_offset; int assignee_dotted; /* store the assignee name to the internal buffer * to make it valid after the token buffer has been overwritten */ assignee = stix->c->tok.name; if (clone_assignee(stix, &assignee, &assignee_offset) <= -1) return -1; assignee_loc = stix->c->tok.loc; assignee_dotted = (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT_DOTTED); GET_TOKEN (stix); if (TOKEN_TYPE(stix) == STIX_IOTOK_ASSIGN) { /* assignment expression */ var_info_t var; GET_TOKEN (stix); if (compile_method_expression(stix, 0) <= -1) goto oops; /* compile_method_expression() is called after clone_assignee(). * clone_assignee() may reallocate a single buffer to hold * a series of assigness names. the pointer based operation is * fragile as it can change. use the offset of the cloned * assignee to update the actual pointer after the recursive * compile_method_expression() call */ assignee.ptr = &stix->c->mth.assignees.ptr[assignee_offset]; if (get_variable_info(stix, &assignee, &assignee_loc, assignee_dotted, &var) <= -1) goto oops; switch (var.type) { case VAR_ARGUMENT: /* assigning to an argument is not allowed */ set_syntax_error (stix, STIX_SYNERR_VARARG, &assignee_loc, &assignee); goto oops; case VAR_TEMPORARY: { #if defined(STIX_USE_CTXTEMPVAR) if (stix->c->mth.blk_depth > 0) { stix_oow_t i; /* if a temporary variable is accessed inside a block, * use a special instruction to indicate it */ STIX_ASSERT (stix, var.pos < stix->c->mth.blk_tmprcnt[stix->c->mth.blk_depth]); for (i = stix->c->mth.blk_depth; i > 0; i--) { if (var.pos >= stix->c->mth.blk_tmprcnt[i - 1]) { if (emit_double_param_instruction(stix, (pop? BCODE_POP_INTO_CTXTEMPVAR_0: BCODE_STORE_INTO_CTXTEMPVAR_0), stix->c->mth.blk_depth - i, var.pos - stix->c->mth.blk_tmprcnt[i - 1]) <= -1) return -1; goto temporary_done; } } } #endif if (emit_single_param_instruction (stix, (pop? BCODE_POP_INTO_TEMPVAR_0: BCODE_STORE_INTO_TEMPVAR_0), var.pos) <= -1) goto oops; temporary_done: ret = pop; break; } case VAR_INSTANCE: case VAR_CLASSINST: if (emit_single_param_instruction (stix, (pop? BCODE_POP_INTO_INSTVAR_0: BCODE_STORE_INTO_INSTVAR_0), var.pos) <= -1) goto oops; ret = pop; break; case VAR_CLASS: if (add_literal (stix, (stix_oop_t)var.cls, &index) <= -1 || emit_double_param_instruction (stix, (pop? BCODE_POP_INTO_OBJVAR_0: BCODE_STORE_INTO_OBJVAR_0), var.pos, index) <= -1) goto oops; ret = pop; break; case VAR_GLOBAL: if (add_literal(stix, (stix_oop_t)var.gbl, &index) <= -1 || emit_single_param_instruction(stix, (pop? BCODE_POP_INTO_OBJECT_0: BCODE_STORE_INTO_OBJECT_0), index) <= -1) return -1; ret = pop; break; default: stix->errnum = STIX_EINTERN; goto oops; } } else { /* what is held in assignee is not an assignee any more. * potentially it is a variable or object reference * to be pused on to the stack */ assignee.ptr = &stix->c->mth.assignees.ptr[assignee_offset]; if (compile_basic_expression(stix, &assignee, &assignee_loc, assignee_dotted) <= -1) goto oops; } } else { assignee.len = 0; if (compile_basic_expression(stix, STIX_NULL, STIX_NULL, 0) <= -1) goto oops; } stix->c->mth.assignees.len -= assignee.len; return ret; oops: stix->c->mth.assignees.len -= assignee.len; return -1; } static int compile_block_statement (stix_t* stix) { /* compile_block_statement() is a simpler version of * of compile_method_statement(). it doesn't cater for * popping the stack top */ if (TOKEN_TYPE(stix) == STIX_IOTOK_RETURN) { /* handle the return statement */ GET_TOKEN (stix); if (compile_method_expression(stix, 0) <= -1) return -1; return emit_byte_instruction (stix, BCODE_RETURN_STACKTOP); } else { return compile_method_expression(stix, 0); } } static int compile_method_statement (stix_t* stix) { /* * method-statement := method-return-statement | method-expression * method-return-statement := "^" method-expression */ if (TOKEN_TYPE(stix) == STIX_IOTOK_RETURN) { /* handle the return statement */ GET_TOKEN (stix); if (compile_method_expression(stix, 0) <= -1) return -1; return emit_byte_instruction (stix, BCODE_RETURN_STACKTOP); } else { /* TODO: optimization. if expresssion is a literal, no push and pop are required */ int n; /* the second parameter to compile_method_expression() indicates * that the stack top will eventually be popped off. the compiler * can optimize some instruction sequencese. for example, two * consecutive store and pop intructions can be transformed to * a more specialized single pop-and-store instruction. */ n = compile_method_expression(stix, 1); if (n <= -1) return -1; /* if n is 1, no stack popping is required */ if (n == 0) { return emit_byte_instruction (stix, BCODE_POP_STACKTOP); } return 0; } } static int compile_method_statements (stix_t* stix) { /* * method-statements := method-statement ("." | ("." method-statements))* */ if (TOKEN_TYPE(stix) != STIX_IOTOK_EOF && TOKEN_TYPE(stix) != STIX_IOTOK_RBRACE) { do { if (compile_method_statement(stix) <= -1) return -1; if (TOKEN_TYPE(stix) == STIX_IOTOK_PERIOD) { /* period after a statement */ GET_TOKEN (stix); if (TOKEN_TYPE(stix) == STIX_IOTOK_EOF || TOKEN_TYPE(stix) == STIX_IOTOK_RBRACE) break; } else { if (TOKEN_TYPE(stix) == STIX_IOTOK_EOF || TOKEN_TYPE(stix) == STIX_IOTOK_RBRACE) break; /* not a period, EOF, nor } */ set_syntax_error (stix, STIX_SYNERR_PERIOD, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } } while (1); } /* arrange to return the receiver if execution reached * the end of the method without explicit return */ return emit_byte_instruction (stix, BCODE_RETURN_RECEIVER); } static int add_compiled_method (stix_t* stix) { stix_oop_char_t name; /* selector */ stix_oop_method_t mth; /* method */ #if defined(STIX_USE_OBJECT_TRAILER) /* nothing extra */ #else stix_oop_byte_t code; #endif stix_oow_t tmp_count = 0; stix_oow_t i; stix_ooi_t preamble_code, preamble_index, preamble_flags; name = (stix_oop_char_t)stix_makesymbol (stix, stix->c->mth.name.ptr, stix->c->mth.name.len); if (!name) return -1; stix_pushtmp (stix, (stix_oop_t*)&name); tmp_count++; /* The variadic data part passed to stix_instantiate() is not GC-safe */ #if defined(STIX_USE_OBJECT_TRAILER) mth = (stix_oop_method_t)stix_instantiatewithtrailer (stix, stix->_method, stix->c->mth.literal_count, stix->c->mth.code.ptr, stix->c->mth.code.len); #else mth = (stix_oop_method_t)stix_instantiate (stix, stix->_method, STIX_NULL, stix->c->mth.literal_count); #endif if (!mth) goto oops; for (i = 0; i < stix->c->mth.literal_count; i++) { /* let's do the variadic data initialization here */ mth->slot[i] = stix->c->mth.literals[i]; } stix_pushtmp (stix, (stix_oop_t*)&mth); tmp_count++; #if defined(STIX_USE_OBJECT_TRAILER) /* do nothing */ #else code = (stix_oop_byte_t)stix_instantiate (stix, stix->_byte_array, stix->c->mth.code.ptr, stix->c->mth.code.len); if (!code) goto oops; stix_pushtmp (stix, (stix_oop_t*)&code); tmp_count++; #endif preamble_code = STIX_METHOD_PREAMBLE_NONE; preamble_index = 0; preamble_flags = 0; if (stix->c->mth.pftype <= 0) { /* no primitive is set */ if (stix->c->mth.code.len <= 0) { preamble_code = STIX_METHOD_PREAMBLE_RETURN_RECEIVER; } else { if (stix->c->mth.code.ptr[0] == BCODE_RETURN_RECEIVER) { preamble_code = STIX_METHOD_PREAMBLE_RETURN_RECEIVER; } else if (stix->c->mth.code.len > 1 && stix->c->mth.code.ptr[1] == BCODE_RETURN_STACKTOP) { switch (stix->c->mth.code.ptr[0]) { case BCODE_PUSH_RECEIVER: preamble_code = STIX_METHOD_PREAMBLE_RETURN_RECEIVER; break; case BCODE_PUSH_NIL: preamble_code = STIX_METHOD_PREAMBLE_RETURN_NIL; break; case BCODE_PUSH_TRUE: preamble_code = STIX_METHOD_PREAMBLE_RETURN_TRUE; break; case BCODE_PUSH_FALSE: preamble_code = STIX_METHOD_PREAMBLE_RETURN_FALSE; break; case BCODE_PUSH_NEGONE: preamble_code = STIX_METHOD_PREAMBLE_RETURN_NEGINDEX; preamble_index = 1; break; case BCODE_PUSH_ZERO: preamble_code = STIX_METHOD_PREAMBLE_RETURN_INDEX; preamble_index = 0; break; case BCODE_PUSH_ONE: preamble_code = STIX_METHOD_PREAMBLE_RETURN_INDEX; preamble_index = 1; break; case BCODE_PUSH_TWO: preamble_code = STIX_METHOD_PREAMBLE_RETURN_INDEX; preamble_index = 2; break; case BCODE_PUSH_INSTVAR_0: case BCODE_PUSH_INSTVAR_1: case BCODE_PUSH_INSTVAR_2: case BCODE_PUSH_INSTVAR_3: case BCODE_PUSH_INSTVAR_4: case BCODE_PUSH_INSTVAR_5: case BCODE_PUSH_INSTVAR_6: case BCODE_PUSH_INSTVAR_7: preamble_code = STIX_METHOD_PREAMBLE_RETURN_INSTVAR; preamble_index = stix->c->mth.code.ptr[0] & 0x7; /* low 3 bits */ break; } } else if (stix->c->mth.code.len > STIX_BCODE_LONG_PARAM_SIZE + 1 && stix->c->mth.code.ptr[STIX_BCODE_LONG_PARAM_SIZE + 1] == BCODE_RETURN_STACKTOP) { int i; switch (stix->c->mth.code.ptr[0]) { case BCODE_PUSH_INSTVAR_X: preamble_code = STIX_METHOD_PREAMBLE_RETURN_INSTVAR; goto set_preamble_index; case BCODE_PUSH_INTLIT: preamble_code = STIX_METHOD_PREAMBLE_RETURN_INDEX; goto set_preamble_index; case BCODE_PUSH_NEGINTLIT: preamble_code = STIX_METHOD_PREAMBLE_RETURN_NEGINDEX; goto set_preamble_index; set_preamble_index: preamble_index = 0; for (i = 1; i <= STIX_BCODE_LONG_PARAM_SIZE; i++) { preamble_index = (preamble_index << 8) | stix->c->mth.code.ptr[i]; } if (!STIX_OOI_IN_METHOD_PREAMBLE_INDEX_RANGE(preamble_index)) { /* the index got out of the range */ preamble_code = STIX_METHOD_PREAMBLE_NONE; preamble_index = 0; } } } } } else if (stix->c->mth.pftype == 1) { preamble_code = STIX_METHOD_PREAMBLE_PRIMITIVE; preamble_index = stix->c->mth.pfnum; } else if (stix->c->mth.pftype == 2) { preamble_code = STIX_METHOD_PREAMBLE_NAMED_PRIMITIVE; preamble_index = stix->c->mth.pfnum; /* index to literal frame */ } else if (stix->c->mth.pftype == 3) { preamble_code = STIX_METHOD_PREAMBLE_EXCEPTION; preamble_index = 0; } else { STIX_ASSERT (stix, stix->c->mth.pftype == 4); preamble_code = STIX_METHOD_PREAMBLE_ENSURE; preamble_index = 0; } if (stix->c->mth.variadic /*&& stix->c->mth.tmpr_nargs > 0*/) preamble_flags |= STIX_METHOD_PREAMBLE_FLAG_VARIADIC; STIX_ASSERT (stix, STIX_OOI_IN_METHOD_PREAMBLE_INDEX_RANGE(preamble_index)); mth->owner = stix->c->cls.self_oop; mth->name = name; mth->preamble = STIX_SMOOI_TO_OOP(STIX_METHOD_MAKE_PREAMBLE(preamble_code, preamble_index, preamble_flags)); mth->preamble_data[0] = STIX_SMOOI_TO_OOP(0); mth->preamble_data[1] = STIX_SMOOI_TO_OOP(0); mth->tmpr_count = STIX_SMOOI_TO_OOP(stix->c->mth.tmpr_count); mth->tmpr_nargs = STIX_SMOOI_TO_OOP(stix->c->mth.tmpr_nargs); #if defined(STIX_USE_OBJECT_TRAILER) /* do nothing */ #else mth->code = code; #endif /*TODO: preserve source??? mth->text = stix->c->mth.text the compiler must collect all source method string collected so far. need to write code to collect string. */ #if defined(STIX_DEBUG_COMPILER) stix_decode (stix, mth, &stix->c->cls.fqn); #endif stix_poptmps (stix, tmp_count); tmp_count = 0; #ifdef MTHDIC if (!stix_putatdic(stix, stix->c->cls.mthdic_oop[stix->c->mth.type], (stix_oop_t)name, (stix_oop_t)mth)) goto oops; #else if (!stix_putatdic(stix, stix->c->cls.self_oop->mthdic[stix->c->mth.type], (stix_oop_t)name, (stix_oop_t)mth)) goto oops; #endif return 0; oops: stix_poptmps (stix, tmp_count); return -1; } static int compile_method_definition (stix_t* stix) { /* clear data required to compile a method */ stix->c->mth.type = STIX_METHOD_INSTANCE; stix->c->mth.text.len = 0; stix->c->mth.assignees.len = 0; stix->c->mth.binsels.len = 0; stix->c->mth.kwsels.len = 0; stix->c->mth.name.len = 0; STIX_MEMSET (&stix->c->mth.name_loc, 0, STIX_SIZEOF(stix->c->mth.name_loc)); stix->c->mth.variadic = 0; stix->c->mth.tmprs.len = 0; stix->c->mth.tmpr_count = 0; stix->c->mth.tmpr_nargs = 0; stix->c->mth.literal_count = 0; stix->c->mth.balit_count = 0; stix->c->mth.arlit_count = 0; stix->c->mth.pftype = 0; stix->c->mth.pfnum = 0; stix->c->mth.blk_depth = 0; stix->c->mth.code.len = 0; if (TOKEN_TYPE(stix) == STIX_IOTOK_LPAREN) { /* process method modifiers */ GET_TOKEN (stix); if (is_token_symbol(stix, VOCA_CLASS_S)) { /* method(#class) */ stix->c->mth.type = STIX_METHOD_CLASS; GET_TOKEN (stix); } if (TOKEN_TYPE(stix) != STIX_IOTOK_RPAREN) { /* ) expected */ set_syntax_error (stix, STIX_SYNERR_RPAREN, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); } if (compile_method_name(stix) <= -1) return -1; if (TOKEN_TYPE(stix) != STIX_IOTOK_LBRACE) { /* { expected */ set_syntax_error (stix, STIX_SYNERR_LBRACE, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); if (compile_method_temporaries(stix) <= -1 || compile_method_primitive(stix) <= -1 || compile_method_statements(stix) <= -1) return -1; if (TOKEN_TYPE(stix) != STIX_IOTOK_RBRACE) { /* } expected */ set_syntax_error (stix, STIX_SYNERR_RBRACE, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); /* add a compiled method to the method dictionary */ if (add_compiled_method(stix) <= -1) return -1; return 0; } static int make_defined_class (stix_t* stix) { /* this function make a class object with no functions/methods */ stix_oop_t tmp; stix_oow_t spec, self_spec; int just_made = 0; spec = STIX_CLASS_SPEC_MAKE (stix->c->cls.var_count[VAR_INSTANCE], ((stix->c->cls.flags & CLASS_INDEXED)? 1: 0), stix->c->cls.indexed_type); self_spec = STIX_CLASS_SELFSPEC_MAKE (stix->c->cls.var_count[VAR_CLASS], stix->c->cls.var_count[VAR_CLASSINST]); if (stix->c->cls.self_oop) { /* this is an internally created class object being defined. */ STIX_ASSERT (stix, STIX_CLASSOF(stix, stix->c->cls.self_oop) == stix->_class); STIX_ASSERT (stix, STIX_OBJ_GET_FLAGS_KERNEL (stix->c->cls.self_oop) == 1); if (spec != STIX_OOP_TO_SMOOI(stix->c->cls.self_oop->spec) || self_spec != STIX_OOP_TO_SMOOI(stix->c->cls.self_oop->selfspec)) { /* it conflicts with internal definition */ set_syntax_error (stix, STIX_SYNERR_CLASSCONTRA, &stix->c->cls.fqn_loc, &stix->c->cls.name); return -1; } } else { /* the class variables and class instance variables are placed * inside the class object after the fixed part. */ tmp = stix_instantiate (stix, stix->_class, STIX_NULL, stix->c->cls.var_count[VAR_CLASSINST] + stix->c->cls.var_count[VAR_CLASS]); if (!tmp) return -1; just_made = 1; stix->c->cls.self_oop = (stix_oop_class_t)tmp; STIX_ASSERT (stix, STIX_CLASSOF(stix, stix->c->cls.self_oop) == stix->_class); stix->c->cls.self_oop->spec = STIX_SMOOI_TO_OOP(spec); stix->c->cls.self_oop->selfspec = STIX_SMOOI_TO_OOP(self_spec); } /* TODO: check if the current class definition conflicts with the superclass. * if superclass is byte variable, the current class cannot be word variable or something else. * TODO: TODO: TODO: */ STIX_OBJ_SET_FLAGS_KERNEL (stix->c->cls.self_oop, 2); stix->c->cls.self_oop->superclass = stix->c->cls.super_oop; tmp = stix_makesymbol (stix, stix->c->cls.name.ptr, stix->c->cls.name.len); if (!tmp) return -1; stix->c->cls.self_oop->name = (stix_oop_char_t)tmp; tmp = stix_makestring (stix, stix->c->cls.vars[VAR_INSTANCE].ptr, stix->c->cls.vars[VAR_INSTANCE].len); if (!tmp) return -1; stix->c->cls.self_oop->instvars = (stix_oop_char_t)tmp; tmp = stix_makestring (stix, stix->c->cls.vars[VAR_CLASS].ptr, stix->c->cls.vars[VAR_CLASS].len); if (!tmp) return -1; stix->c->cls.self_oop->classvars = (stix_oop_char_t)tmp; tmp = stix_makestring (stix, stix->c->cls.vars[VAR_CLASSINST].ptr, stix->c->cls.vars[VAR_CLASSINST].len); if (!tmp) return -1; stix->c->cls.self_oop->classinstvars = (stix_oop_char_t)tmp; tmp = stix_makestring (stix, stix->c->cls.pooldic.ptr, stix->c->cls.pooldic.len); if (!tmp) return -1; stix->c->cls.self_oop->pooldics = (stix_oop_char_t)tmp; /* TOOD: good dictionary size */ tmp = (stix_oop_t)stix_makedic (stix, stix->_method_dictionary, INSTANCE_METHOD_DICTIONARY_SIZE); if (!tmp) return -1; #ifdef MTHDIC stix->c->cls.mthdic_oop[STIX_METHOD_INSTANCE] = (stix_oop_set_t)tmp; #else stix->c->cls.self_oop->mthdic[STIX_METHOD_INSTANCE] = (stix_oop_set_t)tmp; #endif /* TOOD: good dictionary size */ tmp = (stix_oop_t)stix_makedic (stix, stix->_method_dictionary, CLASS_METHOD_DICTIONARY_SIZE); if (!tmp) return -1; #ifdef MTHDIC stix->c->cls.mthdic_oop[STIX_METHOD_CLASS] = (stix_oop_set_t)tmp; #else stix->c->cls.self_oop->mthdic[STIX_METHOD_CLASS] = (stix_oop_set_t)tmp; #endif /* TODO: initialize more fields??? whatelse. */ /* TODO: update the subclasses field of the superclass if it's not nil */ if (just_made) { /* register the class to the system dictionary */ /*if (!stix_putatsysdic(stix, (stix_oop_t)stix->c->cls.self_oop->name, (stix_oop_t)stix->c->cls.self_oop)) return -1;*/ if (!stix_putatdic(stix, stix->c->cls.ns_oop, (stix_oop_t)stix->c->cls.self_oop->name, (stix_oop_t)stix->c->cls.self_oop)) return -1; } return 0; } static int __compile_class_definition (stix_t* stix, int extend) { /* * class-definition := #class class-modifier? class-name (class-body | class-module-import) * * class-modifier := "(" (#byte | #character | #word | #pointer)? ")" * class-body := "{" variable-definition* method-definition* "}" * class-module-import := from "module-name-string" * * variable-definition := (#dcl | #declare) variable-modifier? variable-list "." * variable-modifier := "(" (#class | #classinst)? ")" * variable-list := identifier* * * method-definition := method method-modifier? method-actual-definition * method-modifier := "(" (#class | #instance)? ")" * method-actual-definition := method-name "{" method-tempraries? method-primitive? method-statements* "}" * * NOTE: when extending a class, class-module-import and variable-definition are not allowed. */ stix_oop_association_t ass; stix_ooch_t modname[STIX_MOD_NAME_LEN_MAX + 1]; stix_oow_t modnamelen = 0; if (!extend && TOKEN_TYPE(stix) == STIX_IOTOK_LPAREN) { /* process class modifiers */ GET_TOKEN (stix); if (is_token_symbol(stix, VOCA_BYTE_S)) { /* class(#byte) */ stix->c->cls.flags |= CLASS_INDEXED; stix->c->cls.indexed_type = STIX_OBJ_TYPE_BYTE; GET_TOKEN (stix); } else if (is_token_symbol(stix, VOCA_CHARACTER_S)) { /* class(#character) */ stix->c->cls.flags |= CLASS_INDEXED; stix->c->cls.indexed_type = STIX_OBJ_TYPE_CHAR; GET_TOKEN (stix); } else if (is_token_symbol(stix, VOCA_HALFWORD_S)) { /* class(#halfword) */ stix->c->cls.flags |= CLASS_INDEXED; stix->c->cls.indexed_type = STIX_OBJ_TYPE_HALFWORD; GET_TOKEN (stix); } else if (is_token_symbol(stix, VOCA_WORD_S)) { /* class(#word) */ stix->c->cls.flags |= CLASS_INDEXED; stix->c->cls.indexed_type = STIX_OBJ_TYPE_WORD; GET_TOKEN (stix); } else if (is_token_symbol(stix, VOCA_POINTER_S)) { /* class(#pointer) */ stix->c->cls.flags |= CLASS_INDEXED; stix->c->cls.indexed_type = STIX_OBJ_TYPE_OOP; GET_TOKEN (stix); } else if (is_token_symbol(stix, VOCA_LIWORD_S)) { /* class(#liword) */ stix->c->cls.flags |= CLASS_INDEXED; stix->c->cls.indexed_type = STIX_OBJ_TYPE_LIWORD; GET_TOKEN (stix); } if (TOKEN_TYPE(stix) != STIX_IOTOK_RPAREN) { set_syntax_error (stix, STIX_SYNERR_RPAREN, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); } if (TOKEN_TYPE(stix) != STIX_IOTOK_IDENT && TOKEN_TYPE(stix) != STIX_IOTOK_IDENT_DOTTED) { /* class name expected. */ set_syntax_error (stix, STIX_SYNERR_IDENT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } #if 0 if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT && is_restricted_word (TOKEN_NAME(stix))) { /* wrong class name */ set_syntax_error (stix, STIX_SYNERR_CLASSNAME, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } #endif /* copy the class name */ if (set_class_fqn(stix, TOKEN_NAME(stix)) <= -1) return -1; stix->c->cls.fqn_loc = stix->c->tok.loc; if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT_DOTTED) { if (preprocess_dotted_name(stix, extend, 0, &stix->c->cls.fqn, &stix->c->cls.fqn_loc, &stix->c->cls.name, &stix->c->cls.ns_oop) <= -1) return -1; } else { stix->c->cls.ns_oop = stix->sysdic; } GET_TOKEN (stix); if (extend) { /* extending class */ STIX_ASSERT (stix, stix->c->cls.flags == 0); /*ass = stix_lookupsysdic(stix, &stix->c->cls.name);*/ ass = stix_lookupdic(stix, stix->c->cls.ns_oop, &stix->c->cls.name); if (ass && STIX_CLASSOF(stix, ass->value) == stix->_class && STIX_OBJ_GET_FLAGS_KERNEL(ass->value) != 1) { /* the value must be a class object. * and it must be either a user-defined(0) or * completed kernel built-in(2). * an incomplete kernel built-in class object(1) can not be * extended */ stix->c->cls.self_oop = (stix_oop_class_t)ass->value; } else { /* only an existing class can be extended. */ set_syntax_error (stix, STIX_SYNERR_CLASSUNDEF, &stix->c->cls.fqn_loc, &stix->c->cls.name); return -1; } stix->c->cls.super_oop = stix->c->cls.self_oop->superclass; STIX_ASSERT (stix, (stix_oop_t)stix->c->cls.super_oop == stix->_nil || STIX_CLASSOF(stix, stix->c->cls.super_oop) == stix->_class); } else { int super_is_nil = 0; STIX_INFO2 (stix, "Defining a class %.*S\n", stix->c->cls.fqn.len, stix->c->cls.fqn.ptr); if (TOKEN_TYPE(stix) == STIX_IOTOK_LPAREN) { /* superclass is specified. new class defintion. * for example, #class Class(Stix) */ GET_TOKEN (stix); /* read superclass name */ /* TODO: multiple inheritance */ if (TOKEN_TYPE(stix) == STIX_IOTOK_NIL) { super_is_nil = 1; } else if (TOKEN_TYPE(stix) != STIX_IOTOK_IDENT && TOKEN_TYPE(stix) != STIX_IOTOK_IDENT_DOTTED) { /* superclass name expected */ set_syntax_error (stix, STIX_SYNERR_IDENT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (set_superclass_fqn(stix, TOKEN_NAME(stix)) <= -1) return -1; stix->c->cls.superfqn_loc = stix->c->tok.loc; if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT_DOTTED) { if (preprocess_dotted_name(stix, 1, 0, &stix->c->cls.superfqn, &stix->c->cls.superfqn_loc, &stix->c->cls.supername, &stix->c->cls.superns_oop) <= -1) return -1; } else { /* if no fully qualified name is specified for the super class name, * the name is searched in the name space that the class being defined * belongs to first and in the 'stix->sysdic'. */ stix->c->cls.superns_oop = stix->c->cls.ns_oop; } GET_TOKEN (stix); if (TOKEN_TYPE(stix) != STIX_IOTOK_RPAREN) { set_syntax_error (stix, STIX_SYNERR_RPAREN, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); } else { super_is_nil = 1; } /*ass = stix_lookupsysdic(stix, &stix->c->cls.name);*/ ass = stix_lookupdic (stix, stix->c->cls.ns_oop, &stix->c->cls.name); if (ass) { if (STIX_CLASSOF(stix, ass->value) != stix->_class || STIX_OBJ_GET_FLAGS_KERNEL(ass->value) > 1) { /* the object found with the name is not a class object * or the the class object found is a fully defined kernel * class object */ set_syntax_error (stix, STIX_SYNERR_CLASSDUP, &stix->c->cls.fqn_loc, &stix->c->cls.name); return -1; } stix->c->cls.self_oop = (stix_oop_class_t)ass->value; } else { /* no class of such a name is found. it's a new definition, * which is normal for most new classes. */ STIX_ASSERT (stix, stix->c->cls.self_oop == STIX_NULL); } if (super_is_nil) { stix->c->cls.super_oop = stix->_nil; } else { /* ass = stix_lookupsysdic(stix, &stix->c->cls.supername); */ ass = stix_lookupdic (stix, stix->c->cls.superns_oop, &stix->c->cls.supername); if (!ass && stix->c->cls.superns_oop != stix->sysdic) ass = stix_lookupdic (stix, stix->sysdic, &stix->c->cls.supername); if (ass && STIX_CLASSOF(stix, ass->value) == stix->_class && STIX_OBJ_GET_FLAGS_KERNEL(ass->value) != 1) { /* the value found must be a class and it must not be * an incomplete internal class object */ stix->c->cls.super_oop = ass->value; } else { /* there is no object with such a name. or, * the object found with the name is not a class object. or, * the class object found is a internally defined kernel * class object. */ set_syntax_error (stix, STIX_SYNERR_CLASSUNDEF, &stix->c->cls.superfqn_loc, &stix->c->cls.superfqn); return -1; } } if (is_token_word (stix, VOCA_FROM)) { GET_TOKEN (stix); if (TOKEN_TYPE(stix) != STIX_IOTOK_STRLIT) { set_syntax_error (stix, STIX_SYNERR_STRING, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (TOKEN_NAME_LEN(stix) < 1 || TOKEN_NAME_LEN(stix) > STIX_MOD_NAME_LEN_MAX || stix_findoochar(TOKEN_NAME_PTR(stix), TOKEN_NAME_LEN(stix), '_')) { set_syntax_error (stix, STIX_SYNERR_MODNAME, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } modnamelen = TOKEN_NAME_LEN(stix); stix_copyoochars (modname, TOKEN_NAME_PTR(stix), modnamelen); GET_TOKEN (stix); } } if (TOKEN_TYPE(stix) != STIX_IOTOK_LBRACE) { set_syntax_error (stix, STIX_SYNERR_LBRACE, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (stix->c->cls.super_oop != stix->_nil) { /* adjust the instance variable count and the class instance variable * count to include that of a superclass */ stix_oop_class_t c; stix_oow_t spec, self_spec; c = (stix_oop_class_t)stix->c->cls.super_oop; spec = STIX_OOP_TO_SMOOI(c->spec); self_spec = STIX_OOP_TO_SMOOI(c->selfspec); stix->c->cls.var_count[VAR_INSTANCE] = STIX_CLASS_SPEC_NAMED_INSTVAR(spec); stix->c->cls.var_count[VAR_CLASSINST] = STIX_CLASS_SELFSPEC_CLASSINSTVAR(self_spec); } GET_TOKEN (stix); if (extend) { stix_oop_char_t pds; /* when a class is extended, a new variable cannot be added */ if (is_token_word(stix, VOCA_DCL) || is_token_word(stix, VOCA_DECLARE)) { set_syntax_error (stix, STIX_SYNERR_DCLBANNED, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } #ifdef MTHDIC /* use the method dictionary of an existing class object */ stix->c->cls.mthdic_oop[STIX_METHOD_INSTANCE] = stix->c->cls.self_oop->mthdic[STIX_METHOD_INSTANCE]; stix->c->cls.mthdic_oop[STIX_METHOD_CLASS] = stix->c->cls.self_oop->mthdic[STIX_METHOD_CLASS]; #endif /* load the pooldic definition from the existing class object */ pds = stix->c->cls.self_oop->pooldics; if ((stix_oop_t)pds != stix->_nil) { stix_ooch_t* ptr, * end; STIX_ASSERT (stix, STIX_CLASSOF(stix, pds) == stix->_string); ptr = pds->slot; end = pds->slot + STIX_OBJ_GET_SIZE(pds); /* this loop handles the pooldics string as if it's a pooldic import. * see compile_class_level_variables() for mostly identical code except token handling */ do { stix_oocs_t last, tok; stix_ioloc_t loc; int dotted = 0; stix_oop_set_t ns_oop; while (ptr < end && is_spacechar(*ptr)) ptr++; if (ptr >= end) break; STIX_MEMSET (&loc, 0, STIX_SIZEOF(loc)); /* fake location */ tok.ptr = ptr; while (ptr < end && !is_spacechar(*ptr)) { if (*ptr == '.') dotted = 1; ptr++; } tok.len = ptr - tok.ptr; STIX_ASSERT (stix, tok.len > 0); if (dotted) { if (preprocess_dotted_name(stix, 0, 0, &tok, &loc, &last, &ns_oop) <= -1) return -1; } else { last = tok; /* it falls back to the name space of the class */ ns_oop = stix->c->cls.ns_oop; } if (import_pool_dictionary(stix, ns_oop, &last, &tok, &loc) <= -1) return -1; } while (1); } } else { /* a new class including an internally defined class object */ while (is_token_word(stix, VOCA_DCL) || is_token_word(stix, VOCA_DECLARE)) { /* variable definition. dcl or declare */ GET_TOKEN (stix); if (compile_class_level_variables(stix) <= -1) return -1; } if (make_defined_class(stix) <= -1) return -1; if (modnamelen > 0) { if (stix_importmod (stix, (stix_oop_t)stix->c->cls.self_oop, modname, modnamelen) <= -1) return -1; } } while (is_token_word(stix, VOCA_METHOD)) { /* method definition. method */ GET_TOKEN (stix); if (compile_method_definition(stix) <= -1) return -1; } if (TOKEN_TYPE(stix) != STIX_IOTOK_RBRACE) { set_syntax_error (stix, STIX_SYNERR_RBRACE, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } #ifdef MTHDIC if (!extend) { /* link the method dictionaries created to the actual class object */ /* TODO: anything else to set? */ stix->c->cls.self_oop->mthdic[STIX_CLASS_MTHDIC_INSTANCE] = stix->c->cls.mthdic_oop[STIX_METHOD_INSTANCE]; stix->c->cls.self_oop->mthdic[STIX_CLASS_MTHDIC_CLASS] = stix->c->cls.mthdic_oop[STIX_METHOD_CLASS]; } #endif GET_TOKEN (stix); return 0; } static int compile_class_definition (stix_t* stix, int extend) { int n; stix_oow_t i; /* reset the structure to hold information about a class to be compiled */ stix->c->cls.flags = 0; stix->c->cls.indexed_type = STIX_OBJ_TYPE_OOP; stix->c->cls.name.len = 0; stix->c->cls.supername.len = 0; STIX_MEMSET (&stix->c->cls.fqn_loc, 0, STIX_SIZEOF(stix->c->cls.fqn_loc)); STIX_MEMSET (&stix->c->cls.superfqn_loc, 0, STIX_SIZEOF(stix->c->cls.superfqn_loc)); STIX_ASSERT (stix, STIX_COUNTOF(stix->c->cls.var_count) == STIX_COUNTOF(stix->c->cls.vars)); for (i = 0; i < STIX_COUNTOF(stix->c->cls.var_count); i++) { stix->c->cls.var_count[i] = 0; stix->c->cls.vars[i].len = 0; } stix->c->cls.pooldic_count = 0; stix->c->cls.pooldic.len = 0; stix->c->cls.self_oop = STIX_NULL; stix->c->cls.super_oop = STIX_NULL; #ifdef MTHDIC stix->c->cls.mthdic_oop[STIX_METHOD_INSTANCE] = STIX_NULL; stix->c->cls.mthdic_oop[STIX_METHOD_CLASS] = STIX_NULL; #endif stix->c->cls.ns_oop = STIX_NULL; stix->c->cls.superns_oop = STIX_NULL; stix->c->mth.literal_count = 0; stix->c->mth.balit_count = 0; stix->c->mth.arlit_count = 0; /* do main compilation work */ n = __compile_class_definition (stix, extend); /* reset these oops plus literal pointers not to confuse gc_compiler() */ stix->c->cls.self_oop = STIX_NULL; stix->c->cls.super_oop = STIX_NULL; #ifdef MTHDIC stix->c->cls.mthdic_oop[STIX_METHOD_INSTANCE] = STIX_NULL; stix->c->cls.mthdic_oop[STIX_METHOD_CLASS] = STIX_NULL; #endif stix->c->cls.ns_oop = STIX_NULL; stix->c->cls.superns_oop = STIX_NULL; stix->c->mth.literal_count = 0; stix->c->mth.balit_count = 0; stix->c->mth.arlit_count = 0; stix->c->cls.pooldic_count = 0; return n; } static int __compile_pooldic_definition (stix_t* stix) { stix_oop_t lit; stix_ooi_t tally; stix_oow_t i; if (TOKEN_TYPE(stix) != STIX_IOTOK_IDENT && TOKEN_TYPE(stix) != STIX_IOTOK_IDENT_DOTTED) { set_syntax_error (stix, STIX_SYNERR_IDENT, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } /* [NOTE] * reuse stix->c->cls.fqn and related fields are reused * to store the pool dictionary name */ if (set_class_fqn(stix, TOKEN_NAME(stix)) <= -1) return -1; stix->c->cls.fqn_loc = stix->c->tok.loc; if (TOKEN_TYPE(stix) == STIX_IOTOK_IDENT_DOTTED) { if (preprocess_dotted_name(stix, 0, 0, &stix->c->cls.fqn, &stix->c->cls.fqn_loc, &stix->c->cls.name, &stix->c->cls.ns_oop) <= -1) return -1; } else { stix->c->cls.ns_oop = stix->sysdic; } if (stix_lookupdic (stix, stix->c->cls.ns_oop, &stix->c->cls.name)) { /* a conflicting entry has been found */ set_syntax_error (stix, STIX_SYNERR_POOLDICDUP, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); if (TOKEN_TYPE(stix) != STIX_IOTOK_LBRACE) { set_syntax_error (stix, STIX_SYNERR_LBRACE, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } STIX_INFO2 (stix, "Defining a pool dictionary %.*S\n", stix->c->cls.fqn.len, stix->c->cls.fqn.ptr); GET_TOKEN (stix); while (TOKEN_TYPE(stix) == STIX_IOTOK_SYMLIT) { lit = stix_makesymbol (stix, TOKEN_NAME_PTR(stix) + 1, TOKEN_NAME_LEN(stix) - 1); if (!lit || add_to_array_literal_buffer (stix, lit) <= -1) return -1; GET_TOKEN (stix); if (TOKEN_TYPE(stix) != STIX_IOTOK_ASSIGN) { set_syntax_error (stix, STIX_SYNERR_ASSIGN, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); switch (TOKEN_TYPE(stix)) { case STIX_IOTOK_NIL: lit = stix->_nil; goto add_literal; case STIX_IOTOK_TRUE: lit = stix->_true; goto add_literal; case STIX_IOTOK_FALSE: lit = stix->_false; goto add_literal; case STIX_IOTOK_ERROR: lit = STIX_ERROR_TO_OOP(STIX_EGENERIC); goto add_literal; case STIX_IOTOK_ERRLIT: lit = string_to_error (stix, TOKEN_NAME(stix)); goto add_literal; case STIX_IOTOK_CHARLIT: STIX_ASSERT (stix, TOKEN_NAME_LEN(stix) == 1); lit = STIX_CHAR_TO_OOP(TOKEN_NAME_PTR(stix)[0]); goto add_literal; case STIX_IOTOK_STRLIT: lit = stix_instantiate (stix, stix->_string, TOKEN_NAME_PTR(stix), TOKEN_NAME_LEN(stix)); if (!lit) return -1; goto add_literal; case STIX_IOTOK_SYMLIT: lit = stix_makesymbol (stix, TOKEN_NAME_PTR(stix) + 1, TOKEN_NAME_LEN(stix) - 1); if (!lit) return -1; goto add_literal; case STIX_IOTOK_NUMLIT: case STIX_IOTOK_RADNUMLIT: lit = string_to_num (stix, TOKEN_NAME(stix), TOKEN_TYPE(stix) == STIX_IOTOK_RADNUMLIT); if (!lit) return -1; goto add_literal; case STIX_IOTOK_BAPAREN: /* #[ - byte array parenthesis */ if (read_byte_array_literal(stix, &lit) <= -1) return -1; goto add_literal; case STIX_IOTOK_ARPAREN: /* #( - array parenthesis */ if (read_array_literal(stix, &lit) <= -1) return -1; goto add_literal; add_literal: /* * for this definition, #pooldic MyPoolDic { #a := 10. #b := 20 }, * arlit_buffer contains (#a 10 #b 20) when the 'while' loop is over. */ if (add_to_array_literal_buffer(stix, lit) <= -1) return -1; GET_TOKEN (stix); break; default: set_syntax_error (stix, STIX_SYNERR_LITERAL, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } /*if (TOKEN_TYPE(stix) == STIX_IOTOK_RBRACE) goto done; else*/ if (TOKEN_TYPE(stix) != STIX_IOTOK_PERIOD) { set_syntax_error (stix, STIX_SYNERR_PERIOD, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } GET_TOKEN (stix); } if (TOKEN_TYPE(stix) != STIX_IOTOK_RBRACE) { set_syntax_error (stix, STIX_SYNERR_RBRACE, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } /*done:*/ GET_TOKEN (stix); tally = stix->c->mth.arlit_count / 2; /*TODO: tally and arlit_count range check */ /*if (!STIX_IN_SMOOI_RANGE(tally)) ERROR??*/ /* i use mthdic_oop[0] when compling #pooldic. it's not a real method dictionary. * i just use it not to declare another field into the compiler */ stix->c->cls.pooldic_oop = stix_makedic (stix, stix->_pool_dictionary, STIX_ALIGN(tally + 10, POOL_DICTIONARY_SIZE_ALIGN)); if (!stix->c->cls.pooldic_oop) return -1; for (i = 0; i < stix->c->mth.arlit_count; i += 2) { /* TODO: handle duplicate keys? */ if (!stix_putatdic(stix, stix->c->cls.pooldic_oop, stix->c->mth.arlit[i], stix->c->mth.arlit[i + 1])) return -1; } /* eveything seems ok. register the pool dictionary to the main * system dictionary or to the name space it belongs to */ lit = stix_makesymbol (stix, stix->c->cls.name.ptr, stix->c->cls.name.len); if (!lit || !stix_putatdic (stix, stix->c->cls.ns_oop, lit, (stix_oop_t)stix->c->cls.pooldic_oop)) return -1; return 0; } static int compile_pooldic_definition (stix_t* stix) { int n; /* reset the structure to hold information about a pool dictionary to be compiled. * i'll be reusing some fields reserved for compling a class */ stix->c->cls.name.len = 0; STIX_MEMSET (&stix->c->cls.fqn_loc, 0, STIX_SIZEOF(stix->c->cls.fqn_loc)); stix->c->cls.pooldic_oop = STIX_NULL; stix->c->cls.ns_oop = STIX_NULL; stix->c->mth.balit_count = 0; stix->c->mth.arlit_count = 0; n = __compile_pooldic_definition (stix); /* reset these oops plus literal pointers not to confuse gc_compiler() */ stix->c->cls.pooldic_oop = STIX_NULL; stix->c->cls.ns_oop = STIX_NULL; stix->c->mth.balit_count = 0; stix->c->mth.arlit_count = 0; return n; } static int compile_stream (stix_t* stix) { GET_TOKEN (stix); while (TOKEN_TYPE(stix) != STIX_IOTOK_EOF) { if (is_token_symbol(stix, VOCA_INCLUDE_S)) { /* #include 'xxxx' */ GET_TOKEN (stix); if (TOKEN_TYPE(stix) != STIX_IOTOK_STRLIT) { set_syntax_error (stix, STIX_SYNERR_STRING, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } if (begin_include(stix) <= -1) return -1; } else if (is_token_word(stix, VOCA_CLASS)) { /* class Selfclass(Superclass) { } */ GET_TOKEN (stix); if (compile_class_definition(stix, 0) <= -1) return -1; } else if (is_token_word(stix, VOCA_EXTEND)) { /* extend Selfclass {} */ GET_TOKEN (stix); if (compile_class_definition(stix, 1) <= -1) return -1; } else if (is_token_word(stix, VOCA_POOLDIC)) { /* pooldic SharedPoolDic { #ABC := 20. #DEFG := 'ayz' } */ GET_TOKEN (stix); if (compile_pooldic_definition(stix) <= -1) return -1; } #if 0 else if (is_token_symbol(stix, VOCA_MAIN)) { /* #main */ /* TODO: implement this */ } #endif else { set_syntax_error(stix, STIX_SYNERR_DIRECTIVE, TOKEN_LOC(stix), TOKEN_NAME(stix)); return -1; } } return 0; } static void gc_compiler (stix_t* stix) { /* called when garbage collection is performed */ if (stix->c) { stix_oow_t i; if (stix->c->cls.self_oop) stix->c->cls.self_oop = (stix_oop_class_t)stix_moveoop (stix, (stix_oop_t)stix->c->cls.self_oop); if (stix->c->cls.super_oop) stix->c->cls.super_oop = stix_moveoop (stix, stix->c->cls.super_oop); #ifdef MTHDIC if (stix->c->cls.mthdic_oop[STIX_METHOD_INSTANCE]) stix->c->cls.mthdic_oop[STIX_METHOD_INSTANCE] = (stix_oop_set_t)stix_moveoop (stix, (stix_oop_t)stix->c->cls.mthdic_oop[STIX_METHOD_INSTANCE]); if (stix->c->cls.mthdic_oop[STIX_METHOD_CLASS]) stix->c->cls.mthdic_oop[STIX_METHOD_CLASS] = (stix_oop_set_t)stix_moveoop (stix, (stix_oop_t)stix->c->cls.mthdic_oop[STIX_METHOD_CLASS]); #endif if (stix->c->cls.pooldic_oop) stix->c->cls.pooldic_oop = (stix_oop_set_t)stix_moveoop (stix, (stix_oop_t)stix->c->cls.pooldic_oop); if (stix->c->cls.ns_oop) stix->c->cls.ns_oop = (stix_oop_set_t)stix_moveoop (stix, (stix_oop_t)stix->c->cls.ns_oop); if (stix->c->cls.superns_oop) stix->c->cls.superns_oop = (stix_oop_set_t)stix_moveoop (stix, (stix_oop_t)stix->c->cls.superns_oop); for (i = 0; i < stix->c->cls.pooldic_count; i++) { stix->c->cls.pooldic_imp_oops[i] = (stix_oop_set_t)stix_moveoop (stix, (stix_oop_t)stix->c->cls.pooldic_imp_oops[i]); } for (i = 0; i < stix->c->mth.literal_count; i++) { stix->c->mth.literals[i] = stix_moveoop (stix, stix->c->mth.literals[i]); } for (i = 0; i < stix->c->mth.arlit_count; i++) { stix->c->mth.arlit[i] = stix_moveoop (stix, stix->c->mth.arlit[i]); } } } static void fini_compiler (stix_t* stix) { /* called before the stix object is closed */ if (stix->c) { stix_oow_t i; clear_io_names (stix); if (stix->c->tok.name.ptr) stix_freemem (stix, stix->c->tok.name.ptr); if (stix->c->cls.fqn.ptr) stix_freemem (stix, stix->c->cls.fqn.ptr); if (stix->c->cls.superfqn.ptr) stix_freemem (stix, stix->c->cls.superfqn.ptr); for (i = 0; i < STIX_COUNTOF(stix->c->cls.vars); i++) { if (stix->c->cls.vars[i].ptr) stix_freemem (stix, stix->c->cls.vars[i].ptr); } if (stix->c->cls.pooldic.ptr) stix_freemem (stix, stix->c->cls.pooldic.ptr); if (stix->c->cls.pooldic_imp_oops) stix_freemem (stix, stix->c->cls.pooldic_imp_oops); if (stix->c->mth.text.ptr) stix_freemem (stix, stix->c->mth.text.ptr); if (stix->c->mth.assignees.ptr) stix_freemem (stix, stix->c->mth.assignees.ptr); if (stix->c->mth.binsels.ptr) stix_freemem (stix, stix->c->mth.binsels.ptr); if (stix->c->mth.kwsels.ptr) stix_freemem (stix, stix->c->mth.kwsels.ptr); if (stix->c->mth.name.ptr) stix_freemem (stix, stix->c->mth.name.ptr); if (stix->c->mth.tmprs.ptr) stix_freemem (stix, stix->c->mth.tmprs.ptr); if (stix->c->mth.code.ptr) stix_freemem (stix, stix->c->mth.code.ptr); if (stix->c->mth.literals) stix_freemem (stix, stix->c->mth.literals); if (stix->c->mth.balit) stix_freemem (stix, stix->c->mth.balit); if (stix->c->mth.arlit) stix_freemem (stix, stix->c->mth.arlit); if (stix->c->mth.blk_tmprcnt) stix_freemem (stix, stix->c->mth.blk_tmprcnt); stix_freemem (stix, stix->c); stix->c = STIX_NULL; } } int stix_compile (stix_t* stix, stix_ioimpl_t io) { int n; if (!io) { stix->errnum = STIX_EINVAL; return -1; } if (!stix->c) { stix_cb_t cb, * cbp; STIX_MEMSET (&cb, 0, STIX_SIZEOF(cb)); cb.gc = gc_compiler; cb.fini = fini_compiler; cbp = stix_regcb (stix, &cb); if (!cbp) return -1; stix->c = stix_callocmem (stix, STIX_SIZEOF(*stix->c)); if (!stix->c) { stix_deregcb (stix, cbp); return -1; } stix->c->ilchr_ucs.ptr = &stix->c->ilchr; stix->c->ilchr_ucs.len = 1; } /* Some IO names could have been stored in earlier calls to this function. * I clear such names before i begin this function. i don't clear it * at the end of this function because i may be referenced as an error * location */ clear_io_names (stix); /* initialize some key fields */ stix->c->impl = io; stix->c->nungots = 0; /* The name field and the includer field are STIX_NULL * for the main stream */ STIX_MEMSET (&stix->c->arg, 0, STIX_SIZEOF(stix->c->arg)); stix->c->arg.line = 1; stix->c->arg.colm = 1; /* open the top-level stream */ n = stix->c->impl (stix, STIX_IO_OPEN, &stix->c->arg); if (n <= -1) return -1; /* the stream is open. set it as the current input stream */ stix->c->curinp = &stix->c->arg; /* compile the contents of the stream */ if (compile_stream (stix) <= -1) goto oops; /* close the stream */ STIX_ASSERT (stix, stix->c->curinp == &stix->c->arg); stix->c->impl (stix, STIX_IO_CLOSE, stix->c->curinp); return 0; oops: /* an error occurred and control has reached here * probably, some included files might not have been * closed. close them */ while (stix->c->curinp != &stix->c->arg) { stix_ioarg_t* prev; /* nothing much to do about a close error */ stix->c->impl (stix, STIX_IO_CLOSE, stix->c->curinp); prev = stix->c->curinp->includer; STIX_ASSERT (stix, stix->c->curinp->name != STIX_NULL); STIX_MMGR_FREE (stix->mmgr, stix->c->curinp); stix->c->curinp = prev; } stix->c->impl (stix, STIX_IO_CLOSE, stix->c->curinp); return -1; } void stix_getsynerr (stix_t* stix, stix_synerr_t* synerr) { STIX_ASSERT (stix, stix->c != STIX_NULL); if (synerr) *synerr = stix->c->synerr; }