| /* |
| * NASM-compatible parser |
| * |
| * Copyright (C) 2001-2007 Peter Johnson, Michael Urman |
| * |
| * 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 AND OTHER CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS 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 <util.h> |
| |
| #include <libyasm.h> |
| |
| #include <math.h> |
| |
| #include "modules/parsers/nasm/nasm-parser.h" |
| #include "modules/preprocs/nasm/nasm.h" |
| |
| typedef enum { |
| NORM_EXPR, |
| DIR_EXPR, /* Can't have seg:off or WRT anywhere */ |
| DV_EXPR /* Can't have registers anywhere */ |
| } expr_type; |
| |
| static yasm_bytecode *parse_line(yasm_parser_nasm *parser_nasm); |
| static int parse_directive_valparams(yasm_parser_nasm *parser_nasm, |
| /*@out@*/ yasm_valparamhead *vps); |
| static yasm_bytecode *parse_times(yasm_parser_nasm *parser_nasm); |
| static yasm_bytecode *parse_exp(yasm_parser_nasm *parser_nasm); |
| static yasm_bytecode *parse_instr(yasm_parser_nasm *parser_nasm); |
| static yasm_insn_operand *parse_operand(yasm_parser_nasm *parser_nasm); |
| static yasm_insn_operand *parse_memaddr(yasm_parser_nasm *parser_nasm); |
| static yasm_expr *parse_expr(yasm_parser_nasm *parser_nasm, expr_type type); |
| static yasm_expr *parse_bexpr(yasm_parser_nasm *parser_nasm, expr_type type); |
| static yasm_expr *parse_expr0(yasm_parser_nasm *parser_nasm, expr_type type); |
| static yasm_expr *parse_expr1(yasm_parser_nasm *parser_nasm, expr_type type); |
| static yasm_expr *parse_expr2(yasm_parser_nasm *parser_nasm, expr_type type); |
| static yasm_expr *parse_expr3(yasm_parser_nasm *parser_nasm, expr_type type); |
| static yasm_expr *parse_expr4(yasm_parser_nasm *parser_nasm, expr_type type); |
| static yasm_expr *parse_expr5(yasm_parser_nasm *parser_nasm, expr_type type); |
| static yasm_expr *parse_expr6(yasm_parser_nasm *parser_nasm, expr_type type); |
| |
| static void nasm_parser_directive |
| (yasm_parser_nasm *parser_nasm, const char *name, |
| /*@null@*/ yasm_valparamhead *valparams, |
| /*@null@*/ yasm_valparamhead *objext_valparams); |
| static void set_nonlocal_label(yasm_parser_nasm *parser_nasm, const char *name); |
| static void define_label(yasm_parser_nasm *parser_nasm, /*@only@*/ char *name, |
| unsigned int size); |
| |
| static void yasm_ea_set_implicit_size_segment(yasm_parser_nasm *parser_nasm, |
| yasm_effaddr *ea, yasm_expr *e) |
| { |
| if (parser_nasm->tasm) { |
| const char *segment = yasm_expr_segment(e); |
| ea->data_len = yasm_expr_size(e); |
| if (segment) { |
| const char *segreg = tasm_get_segment_register(segment); |
| if (segreg) |
| yasm_arch_parse_check_regtmod(p_object->arch, segreg, |
| strlen(segreg), &ea->segreg); |
| } |
| } |
| } |
| |
| |
| #define is_eol_tok(tok) ((tok) == 0) |
| #define is_eol() is_eol_tok(curtok) |
| |
| #define get_next_token() (curtok = nasm_parser_lex(&curval, parser_nasm)) |
| |
| static void |
| get_peek_token(yasm_parser_nasm *parser_nasm) |
| { |
| char savech = parser_nasm->tokch; |
| if (parser_nasm->peek_token != NONE) |
| yasm_internal_error(N_("only can have one token of lookahead")); |
| parser_nasm->peek_token = |
| nasm_parser_lex(&parser_nasm->peek_tokval, parser_nasm); |
| parser_nasm->peek_tokch = parser_nasm->tokch; |
| parser_nasm->tokch = savech; |
| } |
| |
| static void |
| destroy_curtok_(yasm_parser_nasm *parser_nasm) |
| { |
| if (curtok < 256) |
| ; |
| else switch ((enum tokentype)curtok) { |
| case INTNUM: |
| yasm_intnum_destroy(curval.intn); |
| break; |
| case FLTNUM: |
| yasm_floatnum_destroy(curval.flt); |
| break; |
| case DIRECTIVE_NAME: |
| case FILENAME: |
| case ID: |
| case LOCAL_ID: |
| case SPECIAL_ID: |
| case NONLOCAL_ID: |
| yasm_xfree(curval.str_val); |
| break; |
| case STRING: |
| yasm_xfree(curval.str.contents); |
| break; |
| case INSN: |
| yasm_bc_destroy(curval.bc); |
| break; |
| default: |
| break; |
| } |
| curtok = NONE; /* sanity */ |
| } |
| #define destroy_curtok() destroy_curtok_(parser_nasm) |
| |
| /* Eat all remaining tokens to EOL, discarding all of them. If there's any |
| * intervening tokens, generates an error (junk at end of line). |
| */ |
| static void |
| demand_eol_(yasm_parser_nasm *parser_nasm) |
| { |
| if (is_eol()) |
| return; |
| |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("junk at end of line, first unrecognized character is `%c'"), |
| parser_nasm->tokch); |
| |
| do { |
| destroy_curtok(); |
| get_next_token(); |
| } while (!is_eol()); |
| } |
| #define demand_eol() demand_eol_(parser_nasm) |
| |
| static const char * |
| describe_token(int token) |
| { |
| static char strch[] = "` '"; |
| const char *str; |
| |
| switch (token) { |
| case 0: str = "end of line"; break; |
| case INTNUM: str = "integer"; break; |
| case FLTNUM: str = "floating point value"; break; |
| case DIRECTIVE_NAME: str = "directive name"; break; |
| case FILENAME: str = "filename"; break; |
| case STRING: str = "string"; break; |
| case SIZE_OVERRIDE: str = "size override"; break; |
| case DECLARE_DATA: str = "DB/DW/etc."; break; |
| case RESERVE_SPACE: str = "RESB/RESW/etc."; break; |
| case INCBIN: str = "INCBIN"; break; |
| case EQU: str = "EQU"; break; |
| case TIMES: str = "TIMES"; break; |
| case SEG: str = "SEG"; break; |
| case WRT: str = "WRT"; break; |
| case NOSPLIT: str = "NOSPLIT"; break; |
| case STRICT: str = "STRICT"; break; |
| case INSN: str = "instruction"; break; |
| case PREFIX: str = "instruction prefix"; break; |
| case REG: str = "register"; break; |
| case REGGROUP: str = "register group"; break; |
| case SEGREG: str = "segment register"; break; |
| case TARGETMOD: str = "target modifier"; break; |
| case LEFT_OP: str = "<<"; break; |
| case RIGHT_OP: str = ">>"; break; |
| case SIGNDIV: str = "//"; break; |
| case SIGNMOD: str = "%%"; break; |
| case START_SECTION_ID: str = "$$"; break; |
| case ID: str = "identifier"; break; |
| case LOCAL_ID: str = ".identifier"; break; |
| case SPECIAL_ID: str = "..identifier"; break; |
| case NONLOCAL_ID: str = "..@identifier"; break; |
| case LINE: str = "%line"; break; |
| default: |
| strch[1] = token; |
| str = strch; |
| break; |
| } |
| |
| return str; |
| } |
| |
| static int |
| expect_(yasm_parser_nasm *parser_nasm, int token) |
| { |
| if (curtok == token) |
| return 1; |
| |
| yasm_error_set(YASM_ERROR_PARSE, "expected %s", describe_token(token)); |
| destroy_curtok(); |
| return 0; |
| } |
| #define expect(token) expect_(parser_nasm, token) |
| |
| void |
| nasm_parser_parse(yasm_parser_nasm *parser_nasm) |
| { |
| unsigned char *line; |
| while ((line = (unsigned char *) |
| yasm_preproc_get_line(parser_nasm->preproc)) != NULL) { |
| yasm_bytecode *bc = NULL, *temp_bc; |
| |
| parser_nasm->s.bot = line; |
| parser_nasm->s.tok = line; |
| parser_nasm->s.ptr = line; |
| parser_nasm->s.cur = line; |
| parser_nasm->s.lim = line + strlen((char *)line)+1; |
| parser_nasm->s.top = parser_nasm->s.lim; |
| |
| get_next_token(); |
| if (!is_eol()) { |
| bc = parse_line(parser_nasm); |
| demand_eol(); |
| } |
| |
| if (parser_nasm->abspos) { |
| /* If we're inside an absolute section, just add to the absolute |
| * position rather than appending bytecodes to a section. |
| * Only RES* are allowed in an absolute section, so this is easy. |
| */ |
| if (bc) { |
| /*@null@*/ const yasm_expr *numitems, *multiple; |
| unsigned int itemsize; |
| numitems = yasm_bc_reserve_numitems(bc, &itemsize); |
| if (numitems) { |
| yasm_expr *e; |
| e = yasm_expr_create(YASM_EXPR_MUL, |
| yasm_expr_expr(yasm_expr_copy(numitems)), |
| yasm_expr_int(yasm_intnum_create_uint(itemsize)), |
| cur_line); |
| multiple = yasm_bc_get_multiple_expr(bc); |
| if (multiple) |
| e = yasm_expr_create_tree(e, YASM_EXPR_MUL, |
| yasm_expr_copy(multiple), |
| cur_line); |
| parser_nasm->abspos = yasm_expr_create_tree( |
| parser_nasm->abspos, YASM_EXPR_ADD, e, cur_line); |
| } else |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("only RES* allowed within absolute section")); |
| yasm_bc_destroy(bc); |
| } |
| temp_bc = NULL; |
| } else if (bc) { |
| temp_bc = yasm_section_bcs_append(cursect, bc); |
| if (temp_bc) |
| parser_nasm->prev_bc = temp_bc; |
| } else |
| temp_bc = NULL; |
| yasm_errwarn_propagate(parser_nasm->errwarns, cur_line); |
| |
| if (parser_nasm->save_input) |
| yasm_linemap_add_source(parser_nasm->linemap, temp_bc, |
| (char *)line); |
| yasm_linemap_goto_next(parser_nasm->linemap); |
| yasm_xfree(line); |
| } |
| } |
| |
| /* All parse_* functions expect to be called with curtok being their first |
| * token. They should return with curtok being the token *after* their |
| * information. |
| */ |
| |
| static yasm_bytecode * |
| parse_line(yasm_parser_nasm *parser_nasm) |
| { |
| yasm_bytecode *bc; |
| |
| bc = parse_exp(parser_nasm); |
| if (bc) |
| return bc; |
| |
| switch (curtok) { |
| case LINE: /* LINE INTNUM '+' INTNUM FILENAME */ |
| { |
| yasm_intnum *line, *incr; |
| char *filename; |
| |
| get_next_token(); |
| |
| if (!expect(INTNUM)) return NULL; |
| line = INTNUM_val; |
| get_next_token(); |
| |
| if (!expect('+')) return NULL; |
| get_next_token(); |
| |
| if (!expect(INTNUM)) return NULL; |
| incr = INTNUM_val; |
| get_next_token(); |
| |
| if (!expect(FILENAME)) return NULL; |
| filename = FILENAME_val; |
| get_next_token(); |
| |
| /* %line indicates the line number of the *next* line, so subtract |
| * out the increment when setting the line number. |
| */ |
| yasm_linemap_set(parser_nasm->linemap, filename, 0, |
| yasm_intnum_get_uint(line) - yasm_intnum_get_uint(incr), |
| yasm_intnum_get_uint(incr)); |
| yasm_intnum_destroy(line); |
| yasm_intnum_destroy(incr); |
| yasm_xfree(filename); |
| return NULL; |
| } |
| case '[': /* [ directive ] */ |
| { |
| char *dirname; |
| yasm_valparamhead dir_vps; |
| int have_vps = 1; |
| |
| parser_nasm->state = DIRECTIVE; |
| get_next_token(); |
| |
| if (!expect(DIRECTIVE_NAME)) |
| return NULL; |
| dirname = DIRECTIVE_NAME_val; |
| get_next_token(); |
| |
| /* ignore [warning]. TODO: actually implement */ |
| if (yasm__strcasecmp(dirname, "warning") == 0) { |
| yasm_warn_set(YASM_WARN_GENERAL, |
| N_("[warning] directive not supported; ignored")); |
| |
| /* throw away the rest of the directive tokens */ |
| while (!is_eol() && curtok != ']') |
| { |
| destroy_curtok(); |
| get_next_token(); |
| } |
| expect(']'); |
| get_next_token(); |
| return NULL; |
| } |
| |
| if (curtok == ']' || curtok == ':') |
| have_vps = 0; |
| else if (!parse_directive_valparams(parser_nasm, &dir_vps)) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("invalid arguments to [%s]"), dirname); |
| yasm_xfree(dirname); |
| return NULL; |
| } |
| if (curtok == ':') { |
| yasm_valparamhead ext_vps; |
| get_next_token(); |
| if (!parse_directive_valparams(parser_nasm, &ext_vps)) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("invalid arguments to [%s]"), dirname); |
| yasm_xfree(dirname); |
| return NULL; |
| } |
| nasm_parser_directive(parser_nasm, dirname, |
| have_vps ? &dir_vps : NULL, &ext_vps); |
| } else |
| nasm_parser_directive(parser_nasm, dirname, |
| have_vps ? &dir_vps : NULL, NULL); |
| yasm_xfree(dirname); |
| expect(']'); |
| get_next_token(); |
| return NULL; |
| } |
| case TIMES: /* TIMES expr exp */ |
| get_next_token(); |
| return parse_times(parser_nasm); |
| case ID: |
| case SPECIAL_ID: |
| case NONLOCAL_ID: |
| case LOCAL_ID: |
| { |
| char *name = ID_val; |
| int local = parser_nasm->tasm |
| ? (curtok == ID || curtok == LOCAL_ID || |
| (curtok == SPECIAL_ID && name[0] == '@')) |
| : (curtok != ID); |
| unsigned int size = 0; |
| |
| get_next_token(); |
| if (is_eol()) { |
| /* label alone on the line */ |
| yasm_warn_set(YASM_WARN_ORPHAN_LABEL, |
| N_("label alone on a line without a colon might be in error")); |
| if (!local) |
| set_nonlocal_label(parser_nasm, name); |
| define_label(parser_nasm, name, 0); |
| return NULL; |
| } |
| if (curtok == ':') |
| get_next_token(); |
| |
| if (curtok == EQU || (parser_nasm->tasm && curtok == '=')) { |
| /* label EQU expr */ |
| yasm_expr *e; |
| get_next_token(); |
| |
| if (parser_nasm->tasm && curtok == SIZE_OVERRIDE) { |
| size = SIZE_OVERRIDE_val; |
| get_next_token(); |
| } |
| |
| e = parse_expr(parser_nasm, NORM_EXPR); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression expected after %s"), "EQU"); |
| yasm_xfree(name); |
| return NULL; |
| } |
| yasm_symtab_define_equ(p_symtab, name, e, cur_line); |
| yasm_xfree(name); |
| return NULL; |
| } |
| |
| if (parser_nasm->tasm && curtok == LABEL) |
| get_next_token(); |
| |
| if (parser_nasm->tasm && curtok == SIZE_OVERRIDE) { |
| size = SIZE_OVERRIDE_val; |
| get_next_token(); |
| } |
| |
| if (!local) |
| set_nonlocal_label(parser_nasm, name); |
| |
| if (is_eol()) { |
| define_label(parser_nasm, name, size); |
| return NULL; |
| } |
| if (curtok == TIMES) { |
| define_label(parser_nasm, name, size); |
| get_next_token(); |
| return parse_times(parser_nasm); |
| } |
| bc = parse_exp(parser_nasm); |
| if (!parser_nasm->tasm && !bc) |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("instruction expected after label")); |
| if (parser_nasm->tasm && bc && !size) |
| size = yasm_bc_elem_size(bc); |
| define_label(parser_nasm, name, size); |
| return bc; |
| } |
| default: |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("label or instruction expected at start of line")); |
| return NULL; |
| } |
| } |
| |
| static int |
| parse_directive_valparams(yasm_parser_nasm *parser_nasm, |
| /*@out@*/ yasm_valparamhead *vps) |
| { |
| yasm_vps_initialize(vps); |
| for (;;) { |
| yasm_valparam *vp; |
| yasm_expr *e; |
| char *id = NULL; |
| |
| /* Look for value first */ |
| if (curtok == ID) { |
| get_peek_token(parser_nasm); |
| if (parser_nasm->peek_token == '=') { |
| id = ID_val; |
| get_next_token(); /* id */ |
| get_next_token(); /* '=' */ |
| } |
| } |
| |
| /* Look for parameter */ |
| switch (curtok) { |
| case STRING: |
| vp = yasm_vp_create_string(id, STRING_val.contents); |
| get_next_token(); |
| break; |
| case ID: |
| /* We need a peek token, but avoid error if we have one |
| * already; we need to work whether or not we hit the |
| * "value=" if test above. |
| * |
| * We cheat and peek ahead to see if this is just an ID or |
| * the ID is part of an expression. We assume a + or - means |
| * that it's part of an expression (e.g. "x+y" is parsed as |
| * the expression "x+y" and not as "x", "+y"). |
| */ |
| if (parser_nasm->peek_token == NONE) |
| get_peek_token(parser_nasm); |
| switch (parser_nasm->peek_token) { |
| case '|': case '^': case '&': case LEFT_OP: case RIGHT_OP: |
| case '+': case '-': |
| case '*': case '/': case '%': case SIGNDIV: case SIGNMOD: |
| break; |
| default: |
| /* Just an id */ |
| vp = yasm_vp_create_id(id, ID_val, '$'); |
| get_next_token(); |
| goto next; |
| } |
| /*@fallthrough@*/ |
| default: |
| e = parse_expr(parser_nasm, DIR_EXPR); |
| if (!e) { |
| yasm_vps_delete(vps); |
| return 0; |
| } |
| vp = yasm_vp_create_expr(id, e); |
| break; |
| } |
| next: |
| yasm_vps_append(vps, vp); |
| if (curtok == ',') |
| get_next_token(); |
| if (curtok == ']' || curtok == ':' || is_eol()) |
| return 1; |
| } |
| } |
| |
| static yasm_bytecode * |
| parse_times(yasm_parser_nasm *parser_nasm) |
| { |
| yasm_expr *multiple; |
| yasm_bytecode *bc; |
| |
| multiple = parse_bexpr(parser_nasm, DV_EXPR); |
| if (!multiple) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("expression expected after %s"), |
| "TIMES"); |
| return NULL; |
| } |
| bc = parse_exp(parser_nasm); |
| if (!bc) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("instruction expected after TIMES expression")); |
| yasm_expr_destroy(multiple); |
| return NULL; |
| } |
| yasm_bc_set_multiple(bc, multiple); |
| return bc; |
| } |
| |
| static yasm_bytecode * |
| parse_exp(yasm_parser_nasm *parser_nasm) |
| { |
| yasm_bytecode *bc; |
| |
| bc = parse_instr(parser_nasm); |
| if (bc) |
| return bc; |
| |
| switch (curtok) { |
| case DECLARE_DATA: |
| { |
| unsigned int size = DECLARE_DATA_val/8; |
| yasm_datavalhead dvs; |
| yasm_dataval *dv; |
| yasm_expr *e, *e2; |
| |
| get_next_token(); |
| |
| yasm_dvs_initialize(&dvs); |
| for (;;) { |
| if (curtok == STRING) { |
| /* Peek ahead to see if we're in an expr. If we're not, |
| * then generate a real string dataval. |
| */ |
| get_peek_token(parser_nasm); |
| if (parser_nasm->peek_token == ',' |
| || is_eol_tok(parser_nasm->peek_token)) { |
| dv = yasm_dv_create_string(STRING_val.contents, |
| STRING_val.len); |
| get_next_token(); |
| goto dv_done; |
| } |
| } |
| if (curtok == '?') { |
| yasm_dvs_delete(&dvs); |
| get_next_token(); |
| if (! is_eol_tok(curtok)) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("can not handle more than one '?'")); |
| return NULL; |
| } |
| return yasm_bc_create_reserve( |
| p_expr_new_ident(yasm_expr_int( |
| yasm_intnum_create_uint(1))), |
| size, cur_line); |
| } |
| if (!(e = parse_bexpr(parser_nasm, DV_EXPR))) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression or string expected")); |
| yasm_dvs_delete(&dvs); |
| return NULL; |
| } |
| if (curtok == DUP) { |
| get_next_token(); |
| if (curtok != '(') { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected ( after DUP")); |
| goto error; |
| } |
| get_next_token(); |
| if (curtok == '?') { |
| get_next_token(); |
| if (curtok != ')') { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected ) after DUPlicated expression")); |
| goto error; |
| } |
| get_next_token(); |
| if (! is_eol_tok(curtok)) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("can not handle more than one '?'")); |
| goto error; |
| } |
| yasm_dvs_delete(&dvs); |
| return yasm_bc_create_reserve(e, size, cur_line); |
| } else if ((e2 = parse_bexpr(parser_nasm, DV_EXPR))) { |
| if (curtok != ')') { |
| yasm_expr_destroy(e2); |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected ) after DUPlicated expression")); |
| goto error; |
| } |
| get_next_token(); |
| dv = yasm_dv_create_expr(e2); |
| yasm_dv_set_multiple(dv, e); |
| } else { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression or string expected")); |
| error: |
| yasm_expr_destroy(e); |
| yasm_dvs_delete(&dvs); |
| return NULL; |
| } |
| } else |
| dv = yasm_dv_create_expr(e); |
| dv_done: |
| yasm_dvs_append(&dvs, dv); |
| if (is_eol()) |
| break; |
| if (!expect(',')) { |
| yasm_dvs_delete(&dvs); |
| return NULL; |
| } |
| get_next_token(); |
| if (is_eol()) /* allow trailing , on list */ |
| break; |
| } |
| return yasm_bc_create_data(&dvs, size, 0, p_object->arch, |
| cur_line); |
| } |
| case RESERVE_SPACE: |
| { |
| unsigned int size = RESERVE_SPACE_val/8; |
| yasm_expr *e; |
| get_next_token(); |
| e = parse_bexpr(parser_nasm, DV_EXPR); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression expected after %s"), "RESx"); |
| return NULL; |
| } |
| return yasm_bc_create_reserve(e, size, cur_line); |
| } |
| case INCBIN: |
| { |
| char *filename; |
| yasm_expr *start = NULL, *maxlen = NULL; |
| |
| get_next_token(); |
| |
| if (!expect(STRING)) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("filename string expected after INCBIN")); |
| return NULL; |
| } |
| filename = STRING_val.contents; |
| get_next_token(); |
| |
| /* optional start expression */ |
| if (curtok == ',') |
| get_next_token(); |
| if (is_eol()) |
| goto incbin_done; |
| start = parse_bexpr(parser_nasm, DV_EXPR); |
| if (!start) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression expected for INCBIN start")); |
| return NULL; |
| } |
| |
| /* optional maxlen expression */ |
| if (curtok == ',') |
| get_next_token(); |
| if (is_eol()) |
| goto incbin_done; |
| maxlen = parse_bexpr(parser_nasm, DV_EXPR); |
| if (!maxlen) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression expected for INCBIN maximum length")); |
| return NULL; |
| } |
| |
| incbin_done: |
| return yasm_bc_create_incbin(filename, start, maxlen, |
| parser_nasm->linemap, cur_line); |
| } |
| default: |
| return NULL; |
| } |
| } |
| |
| static yasm_bytecode * |
| parse_instr(yasm_parser_nasm *parser_nasm) |
| { |
| yasm_bytecode *bc; |
| |
| switch (curtok) { |
| case INSN: |
| { |
| yasm_insn *insn; |
| bc = INSN_val; |
| insn = yasm_bc_get_insn(bc); |
| |
| get_next_token(); |
| if (is_eol()) |
| return bc; /* no operands */ |
| |
| /* parse operands */ |
| for (;;) { |
| yasm_insn_operand *op = parse_operand(parser_nasm); |
| if (!op) { |
| if (insn->num_operands == 0) |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("unexpected %s after instruction"), |
| describe_token(curtok)); |
| else |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected operand, got %s"), |
| describe_token(curtok)); |
| yasm_bc_destroy(bc); |
| return NULL; |
| } |
| yasm_insn_ops_append(insn, op); |
| |
| if (is_eol()) |
| break; |
| if (!expect(',')) { |
| yasm_bc_destroy(bc); |
| return NULL; |
| } |
| get_next_token(); |
| } |
| return bc; |
| } |
| case PREFIX: |
| { |
| uintptr_t prefix = PREFIX_val; |
| get_next_token(); |
| bc = parse_instr(parser_nasm); |
| if (!bc) |
| bc = yasm_arch_create_empty_insn(p_object->arch, cur_line); |
| yasm_insn_add_prefix(yasm_bc_get_insn(bc), prefix); |
| return bc; |
| } |
| case SEGREG: |
| { |
| uintptr_t segreg = SEGREG_val; |
| get_next_token(); |
| bc = parse_instr(parser_nasm); |
| if (!bc) |
| bc = yasm_arch_create_empty_insn(p_object->arch, cur_line); |
| yasm_insn_add_seg_prefix(yasm_bc_get_insn(bc), segreg); |
| return bc; |
| } |
| default: |
| return NULL; |
| } |
| } |
| |
| static yasm_insn_operand * |
| parse_operand(yasm_parser_nasm *parser_nasm) |
| { |
| yasm_insn_operand *op; |
| switch (curtok) { |
| case '[': |
| { |
| get_next_token(); |
| op = parse_memaddr(parser_nasm); |
| |
| expect(']'); |
| get_next_token(); |
| |
| if (!op) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("memory address expected")); |
| return NULL; |
| } |
| |
| if (parser_nasm->tasm && !is_eol() && curtok != ',') { |
| yasm_expr *e = NULL, *f; |
| yasm_effaddr *ea; |
| |
| switch (op->type) { |
| case YASM_INSN__OPERAND_IMM: |
| e = op->data.val; |
| break; |
| case YASM_INSN__OPERAND_MEMORY: |
| if (op->data.ea->disp.rel) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("relative adressing not supported\n")); |
| return NULL; |
| } |
| e = yasm_expr_copy(op->data.ea->disp.abs); |
| yasm_arch_ea_destroy(p_object->arch, op->data.ea); |
| break; |
| case YASM_INSN__OPERAND_REG: |
| case YASM_INSN__OPERAND_SEGREG: |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("register adressing not supported\n")); |
| return NULL; |
| } |
| yasm_xfree(op); |
| f = parse_bexpr(parser_nasm, NORM_EXPR); |
| if (!f) { |
| yasm_expr_destroy(e); |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after ]")); |
| return NULL; |
| } |
| e = p_expr_new_tree(e, YASM_EXPR_ADD, f); |
| ea = yasm_arch_ea_create(p_object->arch, e); |
| yasm_ea_set_implicit_size_segment(parser_nasm, ea, e); |
| op = yasm_operand_create_mem(ea); |
| } |
| return op; |
| } |
| case OFFSET: |
| { |
| yasm_insn_operand *op2; |
| get_next_token(); |
| if (parser_nasm->masm && curtok == ID && !yasm__strcasecmp(ID_val, "flat")) { |
| get_next_token(); |
| if (curtok == ':') { |
| get_next_token(); |
| } |
| } |
| op = parse_operand(parser_nasm); |
| if (!op) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("memory address expected")); |
| return NULL; |
| } |
| if (op->type == YASM_INSN__OPERAND_IMM) |
| return op; |
| if (op->type != YASM_INSN__OPERAND_MEMORY) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("OFFSET applied to non-memory operand")); |
| return NULL; |
| } |
| if (op->data.ea->disp.rel) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("OFFSET applied to non-absolute memory operand")); |
| return NULL; |
| } |
| if (op->data.ea->disp.abs) |
| op2 = yasm_operand_create_imm(op->data.ea->disp.abs); |
| else |
| op2 = yasm_operand_create_imm(p_expr_new_ident( |
| yasm_expr_int(yasm_intnum_create_uint(0)))); |
| yasm_xfree(op); |
| return op2; |
| } |
| case SEGREG: |
| { |
| uintptr_t segreg = SEGREG_val; |
| get_next_token(); |
| if (parser_nasm->tasm && curtok == ':') { |
| get_next_token(); |
| op = parse_operand(parser_nasm); |
| if (!op) |
| return NULL; |
| if (op->type == YASM_INSN__OPERAND_IMM) { |
| yasm_effaddr *ea = yasm_arch_ea_create(p_object->arch, |
| op->data.val); |
| yasm_insn_operand *op2; |
| yasm_ea_set_implicit_size_segment(parser_nasm, ea, |
| op->data.val); |
| op2 = yasm_operand_create_mem(ea); |
| op2->size = op->size; |
| yasm_xfree(op); |
| op = op2; |
| } |
| if (op->type != YASM_INSN__OPERAND_MEMORY) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("segment applied to non-memory operand")); |
| return NULL; |
| } |
| yasm_ea_set_segreg(op->data.ea, segreg); |
| return op; |
| } |
| op = yasm_operand_create_segreg(segreg); |
| return op; |
| } |
| case REG: |
| op = yasm_operand_create_reg(REG_val); |
| get_next_token(); |
| return op; |
| case REGGROUP: |
| { |
| unsigned long regindex; |
| uintptr_t reg = REGGROUP_val; |
| get_next_token(); /* REGGROUP */ |
| if (curtok != '(') |
| return yasm_operand_create_reg(reg); |
| get_next_token(); /* '(' */ |
| if (!expect(INTNUM)) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("integer register index expected")); |
| return NULL; |
| } |
| regindex = yasm_intnum_get_uint(INTNUM_val); |
| get_next_token(); /* INTNUM */ |
| if (!expect(')')) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("missing closing parenthesis for register index")); |
| return NULL; |
| } |
| get_next_token(); /* ')' */ |
| reg = yasm_arch_reggroup_get_reg(p_object->arch, reg, regindex); |
| if (reg == 0) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("bad register index `%u'"), |
| regindex); |
| return NULL; |
| } |
| return yasm_operand_create_reg(reg); |
| } |
| case STRICT: |
| get_next_token(); |
| op = parse_operand(parser_nasm); |
| if (op) |
| op->strict = 1; |
| return op; |
| case SIZE_OVERRIDE: |
| { |
| unsigned int size = SIZE_OVERRIDE_val; |
| get_next_token(); |
| if (parser_nasm->masm && curtok == ID && !yasm__strcasecmp(ID_val, "ptr")) { |
| get_next_token(); |
| } |
| op = parse_operand(parser_nasm); |
| if (!op) |
| return NULL; |
| if (op->type == YASM_INSN__OPERAND_REG && |
| yasm_arch_get_reg_size(p_object->arch, op->data.reg) != size) |
| yasm_error_set(YASM_ERROR_TYPE, |
| N_("cannot override register size")); |
| else { |
| /* Silently override others unless a warning is turned on. |
| * This is to allow overrides such as: |
| * %define arg1 dword [bp+4] |
| * cmp word arg1, 2 |
| * Which expands to: |
| * cmp word dword [bp+4], 2 |
| */ |
| if (op->size != 0) { |
| if (op->size != size) |
| yasm_warn_set(YASM_WARN_SIZE_OVERRIDE, |
| N_("overriding operand size from %u-bit to %u-bit"), |
| op->size, size); |
| else |
| yasm_warn_set(YASM_WARN_SIZE_OVERRIDE, |
| N_("double operand size override")); |
| } |
| op->size = size; |
| } |
| return op; |
| } |
| case TARGETMOD: |
| { |
| uintptr_t tmod = TARGETMOD_val; |
| get_next_token(); |
| op = parse_operand(parser_nasm); |
| if (op) |
| op->targetmod = tmod; |
| return op; |
| } |
| case ID: |
| case LOCAL_ID: |
| case NONLOCAL_ID: |
| if (parser_nasm->tasm) { |
| get_peek_token(parser_nasm); |
| if (parser_nasm->peek_token == '[') { |
| yasm_symrec *sym = yasm_symtab_use(p_symtab, ID_val, |
| cur_line); |
| yasm_expr *e = p_expr_new_ident(yasm_expr_sym(sym)), *f; |
| yasm_effaddr *ea; |
| yasm_xfree(ID_val); |
| get_next_token(); |
| get_next_token(); |
| f = parse_bexpr(parser_nasm, NORM_EXPR); |
| if (!f) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after [")); |
| return NULL; |
| } |
| e = p_expr_new_tree(e, YASM_EXPR_ADD, f); |
| if (!expect(']')) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("missing closing bracket")); |
| return NULL; |
| } |
| get_next_token(); |
| ea = yasm_arch_ea_create(p_object->arch, e); |
| yasm_ea_set_implicit_size_segment(parser_nasm, ea, e); |
| op = yasm_operand_create_mem(ea); |
| return op; |
| } |
| } |
| /* Fallthrough */ |
| default: |
| { |
| yasm_expr *e = parse_bexpr(parser_nasm, NORM_EXPR); |
| if (!e) |
| return NULL; |
| if (curtok != ':') { |
| if (parser_nasm->tasm && yasm_expr_size(e)) { |
| yasm_effaddr *ea = yasm_arch_ea_create(p_object->arch, e); |
| yasm_ea_set_implicit_size_segment(parser_nasm, ea, e); |
| op = yasm_operand_create_mem(ea); |
| return op; |
| } else if (curtok == '[') { |
| yasm_expr *f; |
| yasm_effaddr *ea; |
| yasm_insn_operand *op2; |
| |
| op = parse_operand(parser_nasm); |
| if (!op) |
| return NULL; |
| |
| f = op->data.ea->disp.abs; |
| e = p_expr_new_tree(e, YASM_EXPR_ADD, f); |
| ea = yasm_arch_ea_create(p_object->arch, e); |
| yasm_ea_set_implicit_size_segment(parser_nasm, ea, e); |
| op2 = yasm_operand_create_mem(ea); |
| |
| yasm_xfree(op); |
| |
| return op2; |
| } else { |
| return yasm_operand_create_imm(e); |
| } |
| } else { |
| yasm_expr *off; |
| get_next_token(); |
| off = parse_bexpr(parser_nasm, NORM_EXPR); |
| if (!off) { |
| yasm_expr_destroy(e); |
| return NULL; |
| } |
| op = yasm_operand_create_imm(off); |
| op->seg = e; |
| return op; |
| } |
| } |
| } |
| } |
| |
| /* memory addresses */ |
| static yasm_insn_operand * |
| parse_memaddr(yasm_parser_nasm *parser_nasm) |
| { |
| yasm_insn_operand *op; |
| switch (curtok) { |
| case SEGREG: |
| { |
| uintptr_t segreg = SEGREG_val; |
| get_next_token(); |
| if (!expect(':')) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("`:' required after segment register")); |
| return NULL; |
| } |
| get_next_token(); |
| op = parse_memaddr(parser_nasm); |
| if (op) |
| yasm_ea_set_segreg(op->data.ea, segreg); |
| return op; |
| } |
| case SIZE_OVERRIDE: |
| { |
| unsigned int size = SIZE_OVERRIDE_val; |
| get_next_token(); |
| op = parse_memaddr(parser_nasm); |
| if (op) |
| op->data.ea->disp.size = size; |
| return op; |
| } |
| case NOSPLIT: |
| get_next_token(); |
| op = parse_memaddr(parser_nasm); |
| if (op) |
| op->data.ea->nosplit = 1; |
| return op; |
| case REL: |
| get_next_token(); |
| op = parse_memaddr(parser_nasm); |
| if (op) { |
| op->data.ea->pc_rel = 1; |
| op->data.ea->not_pc_rel = 0; |
| } |
| return op; |
| case ABS: |
| get_next_token(); |
| op = parse_memaddr(parser_nasm); |
| if (op) { |
| op->data.ea->pc_rel = 0; |
| op->data.ea->not_pc_rel = 1; |
| } |
| return op; |
| default: |
| { |
| yasm_expr *e = parse_bexpr(parser_nasm, NORM_EXPR); |
| if (!e) |
| return NULL; |
| if (curtok != ':') { |
| yasm_effaddr *ea = yasm_arch_ea_create(p_object->arch, e); |
| yasm_ea_set_implicit_size_segment(parser_nasm, ea, e); |
| return yasm_operand_create_mem(ea); |
| } else { |
| yasm_effaddr *ea; |
| yasm_expr *off; |
| get_next_token(); |
| off = parse_bexpr(parser_nasm, NORM_EXPR); |
| if (!off) { |
| yasm_expr_destroy(e); |
| return NULL; |
| } |
| ea = yasm_arch_ea_create(p_object->arch, off); |
| yasm_ea_set_implicit_size_segment(parser_nasm, ea, off); |
| op = yasm_operand_create_mem(ea); |
| op->seg = e; |
| return op; |
| } |
| } |
| } |
| } |
| |
| /* Expression grammar parsed is: |
| * |
| * expr : bexpr [ : bexpr ] |
| * bexpr : expr0 [ WRT expr6 ] |
| * expr0 : expr1 [ {|} expr1...] |
| * expr1 : expr2 [ {^} expr2...] |
| * expr2 : expr3 [ {&} expr3...] |
| * expr3 : expr4 [ {<<,>>} expr4...] |
| * expr4 : expr5 [ {+,-} expr5...] |
| * expr5 : expr6 [ {*,/,%,//,%%} expr6...] |
| * expr6 : { ~,+,-,SEG } expr6 |
| * | (expr) |
| * | symbol |
| * | $ |
| * | number |
| */ |
| |
| #define parse_expr_common(leftfunc, tok, rightfunc, op) \ |
| do { \ |
| yasm_expr *e, *f; \ |
| e = leftfunc(parser_nasm, type); \ |
| if (!e) \ |
| return NULL; \ |
| \ |
| while (curtok == tok) { \ |
| get_next_token(); \ |
| f = rightfunc(parser_nasm, type); \ |
| if (!f) { \ |
| yasm_error_set(YASM_ERROR_SYNTAX, \ |
| N_("expected expression after %s"), \ |
| describe_token(op)); \ |
| yasm_expr_destroy(e); \ |
| return NULL; \ |
| } \ |
| e = p_expr_new_tree(e, op, f); \ |
| } \ |
| return e; \ |
| } while(0) |
| |
| static yasm_expr * |
| parse_expr(yasm_parser_nasm *parser_nasm, expr_type type) |
| { |
| switch (type) { |
| case DIR_EXPR: |
| /* directive expressions can't handle seg:off or WRT */ |
| return parse_expr0(parser_nasm, type); |
| default: |
| parse_expr_common(parse_bexpr, ':', parse_bexpr, YASM_EXPR_SEGOFF); |
| } |
| /*@notreached@*/ |
| return NULL; |
| } |
| |
| static yasm_expr * |
| parse_bexpr(yasm_parser_nasm *parser_nasm, expr_type type) |
| { |
| parse_expr_common(parse_expr0, WRT, parse_expr6, YASM_EXPR_WRT); |
| } |
| |
| static yasm_expr * |
| parse_expr0(yasm_parser_nasm *parser_nasm, expr_type type) |
| { |
| parse_expr_common(parse_expr1, '|', parse_expr1, YASM_EXPR_OR); |
| } |
| |
| static yasm_expr * |
| parse_expr1(yasm_parser_nasm *parser_nasm, expr_type type) |
| { |
| parse_expr_common(parse_expr2, '^', parse_expr2, YASM_EXPR_XOR); |
| } |
| |
| static yasm_expr * |
| parse_expr2(yasm_parser_nasm *parser_nasm, expr_type type) |
| { |
| parse_expr_common(parse_expr3, '&', parse_expr3, YASM_EXPR_AND); |
| } |
| |
| static yasm_expr * |
| parse_expr3(yasm_parser_nasm *parser_nasm, expr_type type) |
| { |
| yasm_expr *e, *f; |
| e = parse_expr4(parser_nasm, type); |
| if (!e) |
| return NULL; |
| |
| while (curtok == LEFT_OP || curtok == RIGHT_OP) { |
| int op = curtok; |
| get_next_token(); |
| f = parse_expr4(parser_nasm, type); |
| if (!f) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after %s"), |
| describe_token(op)); |
| yasm_expr_destroy(e); |
| return NULL; |
| } |
| |
| switch (op) { |
| case LEFT_OP: e = p_expr_new_tree(e, YASM_EXPR_SHL, f); break; |
| case RIGHT_OP: e = p_expr_new_tree(e, YASM_EXPR_SHR, f); break; |
| } |
| } |
| return e; |
| } |
| |
| static yasm_expr * |
| parse_expr4(yasm_parser_nasm *parser_nasm, expr_type type) |
| { |
| yasm_expr *e, *f; |
| e = parse_expr5(parser_nasm, type); |
| if (!e) |
| return NULL; |
| |
| while (curtok == '+' || curtok == '-') { |
| int op = curtok; |
| get_next_token(); |
| f = parse_expr5(parser_nasm, type); |
| if (!f) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after %s"), |
| describe_token(op)); |
| yasm_expr_destroy(e); |
| return NULL; |
| } |
| |
| switch (op) { |
| case '+': e = p_expr_new_tree(e, YASM_EXPR_ADD, f); break; |
| case '-': e = p_expr_new_tree(e, YASM_EXPR_SUB, f); break; |
| } |
| } |
| return e; |
| } |
| |
| static yasm_expr * |
| parse_expr5(yasm_parser_nasm *parser_nasm, expr_type type) |
| { |
| yasm_expr *e, *f; |
| e = parse_expr6(parser_nasm, type); |
| if (!e) |
| return NULL; |
| |
| while (curtok == '*' || curtok == '/' || curtok == '%' |
| || curtok == SIGNDIV || curtok == SIGNMOD) { |
| int op = curtok; |
| get_next_token(); |
| f = parse_expr6(parser_nasm, type); |
| if (!f) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after %s"), |
| describe_token(op)); |
| yasm_expr_destroy(e); |
| return NULL; |
| } |
| |
| switch (op) { |
| case '*': e = p_expr_new_tree(e, YASM_EXPR_MUL, f); break; |
| case '/': e = p_expr_new_tree(e, YASM_EXPR_DIV, f); break; |
| case '%': e = p_expr_new_tree(e, YASM_EXPR_MOD, f); break; |
| case SIGNDIV: e = p_expr_new_tree(e, YASM_EXPR_SIGNDIV, f); break; |
| case SIGNMOD: e = p_expr_new_tree(e, YASM_EXPR_SIGNMOD, f); break; |
| } |
| } |
| return e; |
| } |
| |
| static yasm_expr * |
| parse_expr6(yasm_parser_nasm *parser_nasm, expr_type type) |
| { |
| yasm_expr *e; |
| yasm_symrec *sym; |
| |
| switch (curtok) { |
| case '+': |
| get_next_token(); |
| e = parse_expr6(parser_nasm, type); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after %s"), "`+'"); |
| } |
| return e; |
| case '-': |
| get_next_token(); |
| e = parse_expr6(parser_nasm, type); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after %s"), "`-'"); |
| return NULL; |
| } |
| return p_expr_new_branch(YASM_EXPR_NEG, e); |
| case '~': |
| get_next_token(); |
| e = parse_expr6(parser_nasm, type); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after %s"), "`~'"); |
| return NULL; |
| } |
| return p_expr_new_branch(YASM_EXPR_NOT, e); |
| case LOW: |
| get_next_token(); |
| e = parse_expr6(parser_nasm, type); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after %s"), "LOW"); |
| return NULL; |
| } |
| return p_expr_new_tree(e, YASM_EXPR_AND, |
| p_expr_new_ident(yasm_expr_int(yasm_intnum_create_uint(0xff)))); |
| case HIGH: |
| get_next_token(); |
| e = parse_expr6(parser_nasm, type); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after %s"), "HIGH"); |
| return NULL; |
| } |
| return p_expr_new_tree( |
| p_expr_new_tree(e, YASM_EXPR_SHR, |
| p_expr_new_ident(yasm_expr_int( |
| yasm_intnum_create_uint(8)))), |
| YASM_EXPR_AND, |
| p_expr_new_ident(yasm_expr_int(yasm_intnum_create_uint(0xff)))); |
| case SEG: |
| get_next_token(); |
| e = parse_expr6(parser_nasm, type); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after %s"), "SEG"); |
| return NULL; |
| } |
| return p_expr_new_branch(YASM_EXPR_SEG, e); |
| case '(': |
| get_next_token(); |
| e = parse_expr(parser_nasm, type); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected expression after %s"), "`('"); |
| return NULL; |
| } |
| if (!expect(')')) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("missing parenthesis")); |
| return NULL; |
| } |
| get_next_token(); |
| return e; |
| case INTNUM: |
| e = p_expr_new_ident(yasm_expr_int(INTNUM_val)); |
| get_next_token(); |
| return e; |
| case REG: |
| if (type == DV_EXPR) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("data values can't have registers")); |
| return NULL; |
| } |
| e = p_expr_new_ident(yasm_expr_reg(REG_val)); |
| get_next_token(); |
| return e; |
| } |
| |
| /* directives allow very little and handle IDs specially */ |
| if (type == DIR_EXPR) { |
| switch (curtok) { |
| case ID: |
| sym = yasm_symtab_use(p_symtab, ID_val, cur_line); |
| e = p_expr_new_ident(yasm_expr_sym(sym)); |
| yasm_xfree(ID_val); |
| break; |
| default: |
| return NULL; |
| } |
| } else switch (curtok) { |
| case FLTNUM: |
| e = p_expr_new_ident(yasm_expr_float(FLTNUM_val)); |
| break; |
| case STRING: |
| { |
| yasm_intnum *intn; |
| if (parser_nasm->tasm) |
| intn = yasm_intnum_create_charconst_tasm(STRING_val.contents); |
| else |
| intn = yasm_intnum_create_charconst_nasm(STRING_val.contents); |
| e = p_expr_new_ident(yasm_expr_int(intn)); |
| yasm_xfree(STRING_val.contents); |
| break; |
| } |
| case SPECIAL_ID: |
| sym = yasm_objfmt_get_special_sym(p_object, ID_val+2, "nasm"); |
| if (sym) { |
| e = p_expr_new_ident(yasm_expr_sym(sym)); |
| yasm_xfree(ID_val); |
| break; |
| } |
| /*@fallthrough@*/ |
| case ID: |
| case LOCAL_ID: |
| case NONLOCAL_ID: |
| sym = yasm_symtab_use(p_symtab, ID_val, cur_line); |
| e = p_expr_new_ident(yasm_expr_sym(sym)); |
| yasm_xfree(ID_val); |
| break; |
| case '$': |
| /* "$" references the current assembly position */ |
| if (parser_nasm->abspos) |
| e = yasm_expr_copy(parser_nasm->abspos); |
| else { |
| sym = yasm_symtab_define_curpos(p_symtab, "$", |
| parser_nasm->prev_bc, cur_line); |
| e = p_expr_new_ident(yasm_expr_sym(sym)); |
| } |
| break; |
| case START_SECTION_ID: |
| /* "$$" references the start of the current section */ |
| if (parser_nasm->absstart) |
| e = yasm_expr_copy(parser_nasm->absstart); |
| else { |
| sym = yasm_symtab_define_label(p_symtab, "$$", |
| yasm_section_bcs_first(cursect), 0, cur_line); |
| e = p_expr_new_ident(yasm_expr_sym(sym)); |
| } |
| break; |
| default: |
| return NULL; |
| } |
| |
| get_next_token(); |
| return e; |
| } |
| |
| static void |
| set_nonlocal_label(yasm_parser_nasm *parser_nasm, const char *name) |
| { |
| if (!parser_nasm->tasm || tasm_locals) { |
| if (parser_nasm->locallabel_base) |
| yasm_xfree(parser_nasm->locallabel_base); |
| parser_nasm->locallabel_base_len = strlen(name); |
| parser_nasm->locallabel_base = |
| yasm_xmalloc(parser_nasm->locallabel_base_len+1); |
| strcpy(parser_nasm->locallabel_base, name); |
| } |
| } |
| |
| static void |
| define_label(yasm_parser_nasm *parser_nasm, char *name, unsigned int size) |
| { |
| yasm_symrec *symrec; |
| |
| if (parser_nasm->abspos) |
| symrec = yasm_symtab_define_equ(p_symtab, name, |
| yasm_expr_copy(parser_nasm->abspos), |
| cur_line); |
| else |
| symrec = yasm_symtab_define_label(p_symtab, name, parser_nasm->prev_bc, |
| 1, cur_line); |
| |
| yasm_symrec_set_size(symrec, size); |
| yasm_symrec_set_segment(symrec, tasm_segment); |
| |
| yasm_xfree(name); |
| } |
| |
| static void |
| dir_align(yasm_object *object, yasm_valparamhead *valparams, |
| yasm_valparamhead *objext_valparams, unsigned long line) |
| { |
| yasm_valparam *vp = yasm_vps_first(valparams); |
| yasm_expr *boundval = yasm_vp_expr(vp, object->symtab, line); |
| /*@depedent@*/ yasm_intnum *boundintn; |
| |
| /* Largest .align in the section specifies section alignment. |
| * Note: this doesn't match NASM behavior, but is a lot more |
| * intelligent! |
| */ |
| if (boundval && (boundintn = yasm_expr_get_intnum(&boundval, 0))) { |
| unsigned long boundint = yasm_intnum_get_uint(boundintn); |
| |
| /* Alignments must be a power of two. */ |
| if (is_exp2(boundint)) { |
| if (boundint > yasm_section_get_align(object->cur_section)) |
| yasm_section_set_align(object->cur_section, boundint, line); |
| } |
| } |
| |
| /* As this directive is called only when nop is used as fill, always |
| * use arch (nop) fill. |
| */ |
| yasm_section_bcs_append(object->cur_section, |
| yasm_bc_create_align(boundval, NULL, NULL, |
| /*yasm_section_is_code(object->cur_section) ?*/ |
| yasm_arch_get_fill(object->arch)/* : NULL*/, |
| line)); |
| } |
| |
| static void |
| nasm_parser_directive(yasm_parser_nasm *parser_nasm, const char *name, |
| yasm_valparamhead *valparams, |
| yasm_valparamhead *objext_valparams) |
| { |
| unsigned long line = cur_line; |
| yasm_valparam *vp; |
| |
| if (!yasm_object_directive(p_object, name, "nasm", valparams, |
| objext_valparams, line)) |
| ; |
| else if (yasm__strcasecmp(name, "absolute") == 0) { |
| if (!valparams) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("directive `%s' requires an argument"), |
| "absolute"); |
| } else { |
| vp = yasm_vps_first(valparams); |
| if (parser_nasm->absstart) |
| yasm_expr_destroy(parser_nasm->absstart); |
| if (parser_nasm->abspos) |
| yasm_expr_destroy(parser_nasm->abspos); |
| parser_nasm->absstart = yasm_vp_expr(vp, p_object->symtab, line); |
| parser_nasm->abspos = yasm_expr_copy(parser_nasm->absstart); |
| cursect = NULL; |
| parser_nasm->prev_bc = NULL; |
| } |
| } else if (yasm__strcasecmp(name, "align") == 0) { |
| /* Really, we shouldn't end up with an align directive in an absolute |
| * section (as it's supposed to be only used for nop fill), but handle |
| * it gracefully anyway. |
| */ |
| if (parser_nasm->abspos) { |
| yasm_expr *boundval, *e; |
| vp = yasm_vps_first(valparams); |
| boundval = yasm_vp_expr(vp, p_object->symtab, line); |
| e = yasm_expr_create_tree( |
| yasm_expr_create_tree(yasm_expr_copy(parser_nasm->absstart), |
| YASM_EXPR_SUB, |
| yasm_expr_copy(parser_nasm->abspos), |
| cur_line), |
| YASM_EXPR_AND, |
| yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(boundval), |
| yasm_expr_int(yasm_intnum_create_uint(1)), |
| cur_line), |
| cur_line); |
| parser_nasm->abspos = yasm_expr_create_tree( |
| parser_nasm->abspos, YASM_EXPR_ADD, e, cur_line); |
| } else if (!valparams) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("directive `%s' requires an argument"), "align"); |
| } else |
| dir_align(p_object, valparams, objext_valparams, line); |
| } else if (yasm__strcasecmp(name, "default") == 0) { |
| if (!valparams) |
| ; |
| else { |
| vp = yasm_vps_first(valparams); |
| while (vp) { |
| const char *id = yasm_vp_id(vp); |
| if (id) { |
| if (yasm__strcasecmp(id, "rel") == 0) |
| yasm_arch_set_var(p_object->arch, "default_rel", 1); |
| else if (yasm__strcasecmp(id, "abs") == 0) |
| yasm_arch_set_var(p_object->arch, "default_rel", 0); |
| else |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("unrecognized default `%s'"), id); |
| } else |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("unrecognized default value")); |
| vp = yasm_vps_next(vp); |
| } |
| } |
| } else |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("unrecognized directive `%s'"), |
| name); |
| |
| if (parser_nasm->absstart && cursect) { |
| /* We switched to a new section. Get out of absolute section mode. */ |
| yasm_expr_destroy(parser_nasm->absstart); |
| parser_nasm->absstart = NULL; |
| if (parser_nasm->abspos) { |
| yasm_expr_destroy(parser_nasm->abspos); |
| parser_nasm->abspos = NULL; |
| } |
| } |
| |
| if (cursect) { |
| /* In case cursect changed or a bytecode was added, update prev_bc. */ |
| parser_nasm->prev_bc = yasm_section_bcs_last(cursect); |
| } |
| |
| if (valparams) |
| yasm_vps_delete(valparams); |
| if (objext_valparams) |
| yasm_vps_delete(objext_valparams); |
| } |
| |
| yasm_bytecode * |
| gas_intel_syntax_parse_instr(yasm_parser_nasm *parser_nasm, unsigned char *instr) |
| { |
| yasm_bytecode *bc = NULL; |
| char *sinstr = (char *) instr; |
| |
| parser_nasm->s.bot = instr; |
| parser_nasm->s.tok = instr; |
| parser_nasm->s.ptr = instr; |
| parser_nasm->s.cur = instr; |
| parser_nasm->s.lim = instr + strlen(sinstr) + 1; |
| parser_nasm->s.top = parser_nasm->s.lim; |
| parser_nasm->peek_token = NONE; |
| |
| get_next_token(); |
| if (!is_eol()) { |
| bc = parse_instr(parser_nasm); |
| } |
| |
| return bc; |
| } |