AST * GetComments(void) { AST *ret = comment_chain; comment_chain = NULL; if (!ret) { ret = NewAST(AST_COMMENT, NULL, NULL); // just for holding error lines ret->d.string = NULL; } return ret; }
// // establish an indent level // if the line is indented more than this, a T_INDENT will // be emitted // void EstablishIndent(LexStream *L, int level) { AST *dummy = NewAST(AST_UNKNOWN, NULL, NULL); if (L->indentsp >= MAX_INDENTS-1) { ERROR(dummy, "too many nested indentation levels"); return; } if (level < 0) { level = L->firstNonBlank; } L->indentsp++; L->indent[L->indentsp] = level; L->pending_indent++; }
/* parse a string */ static int parseString(LexStream *L, AST **ast_ptr) { int c; struct flexbuf fb; AST *ast; flexbuf_init(&fb, INCSTR); c = lexgetc(L); while (c != '"' && c > 0 && c < 256) { flexbuf_addchar(&fb, c); c = lexgetc(L); } flexbuf_addchar(&fb, '\0'); ast = NewAST(AST_STRING, NULL, NULL); ast->d.string = flexbuf_get(&fb); *ast_ptr = ast; return T_STRING; }
int getToken(LexStream *L, AST **ast_ptr) { // int base = 10; int c; AST *ast = NULL; int at_startofline = (L->eoln == 1); int peekc; c = skipSpace(L, &ast); if (c >= 127) { *ast_ptr = last_ast = ast; return c; } else if (safe_isdigit(c)) { lexungetc(L,c); ast = NewAST(AST_INTEGER, NULL, NULL); c = parseNumber(L, 10, &ast->d.ival); if (c == T_FLOATNUM) ast->kind = AST_FLOAT; } else if (c == '$') { ast = NewAST(AST_INTEGER, NULL, NULL); c = parseNumber(L, 16, &ast->d.ival); } else if (c == '%') { ast = NewAST(AST_INTEGER, NULL, NULL); c = lexgetc(L); if (c == '%') { c = parseNumber(L, 4, &ast->d.ival); } else { lexungetc(L, c); c = parseNumber(L, 2, &ast->d.ival); } } else if (isIdentifierStart(c)) { lexungetc(L, c); c = parseIdentifier(L, &ast, NULL); /* if in pasm, and at start of line, restart temporary labels */ if (c == T_IDENTIFIER && InDatBlock(L) && at_startofline) { L->lastGlobal = ast->d.string; } } else if (c == ':') { peekc = lexgetc(L); if (peekc == '=') { c = T_ASSIGN; } else if (!gl_p2 && isIdentifierStart(peekc) && InDatBlock(L)) { lexungetc(L, peekc); c = parseIdentifier(L, &ast, L->lastGlobal ? L->lastGlobal : ""); } else { lexungetc(L, peekc); } } else if (gl_p2 && c == '.' && isIdentifierStart(lexpeekc(L)) && InDatBlock(L)) { c = parseIdentifier(L, &ast, L->lastGlobal ? L->lastGlobal : ""); } else if (strchr(operator_chars, c) != NULL) { char op[6]; int i; int token; Symbol *sym = NULL; op[0] = token = c; for (i = 1; i < sizeof(op)-1; i++) { c = lexgetc(L); if (c >= 128 || strchr(operator_chars, c) == NULL) { lexungetc(L, c); break; } op[i] = c; op[i+1] = 0; sym = FindSymbol(&reservedWords, op); if (sym) { token = INTVAL(sym); } else { lexungetc(L, c); break; } } c = token; } else if (c == '"') { c = parseString(L, &ast); } *ast_ptr = last_ast = ast; return c; }
int skipSpace(LexStream *L, AST **ast_ptr) { int c; int commentNest; int start_indent; struct flexbuf cb; AST *ast; int startcol = 0; int startline = 0; flexbuf_init(&cb, INCSTR); c = lexgetc(L); again: while (c == ' ' || c == '\t') { c = lexgetc(L); } /* ignore completely empty lines or ones with just comments */ if (c == '\'') { while (c == '\'') c = lexgetc(L); while (c != '\n' && c != T_EOF) { flexbuf_addchar(&cb, c); c = lexgetc(L); } flexbuf_addchar(&cb, '\n'); flexbuf_addchar(&cb, 0); ast = NewAST(AST_COMMENT, NULL, NULL); ast->d.string = flexbuf_get(&cb); comment_chain = AddToList(comment_chain, ast); } if (c == '{') { struct flexbuf anno; int annotate = 0; int directive = 0; int doccomment = 0; startcol = L->colCounter; startline = L->lineCounter; flexbuf_init(&anno, INCSTR); commentNest = 1; /* check for special comments {++... } which indicate inline C code We also set up the preprocessor to emit {#line xx} directives when doing #include */ c = lexgetc(L); if (c == '+') { c = lexgetc(L); if (c == '+') { annotate = 1; c = lexgetc(L); } } else if (c == '#') { c = lexgetc(L); directive = 1; } else if (c == '{') { c = lexgetc(L); doccomment = 1; } lexungetc(L, c); for(;;) { c = lexgetc(L); if (c == '{' && !doccomment) commentNest++; else if (c == '}') { if (doccomment) { int peekc; peekc = lexgetc(L); if (peekc == '}') { commentNest = 0; } else { lexungetc(L, peekc); } } else { --commentNest; } } if (commentNest <= 0 || c == T_EOF) { break; } if (annotate || directive) { flexbuf_addchar(&anno, c); } else { flexbuf_addchar(&cb, c); } } if (c == T_EOF) { if (commentNest > 0) fprintf(stderr, "WARNING: EOF seen inside comment\n"); return c; } if (annotate) { AST *ast = NewAST(AST_ANNOTATION, NULL, NULL); flexbuf_addchar(&anno, '\0'); ast->d.string = flexbuf_get(&anno); *ast_ptr = ast; // if this is indented and inside a PUB or PRI, // then treat it as inline C code if (startcol > 1 && startline > L->block_firstline && (L->in_block == T_PUB || L->in_block == T_PRI)) { return T_INLINECCODE; } return T_ANNOTATION; } else if (directive) { char *dir; flexbuf_addchar(&anno, '\0'); dir = flexbuf_get(&anno); if (!strncmp(dir, "line ", 5)) { char *ptr = dir+5; int lineno; lineno = strtol(ptr, &ptr, 10); if (lineno > 0) { if (*ptr == ' ') ptr++; L->fileName = strdup(ptr); L->lineCounter = lineno; } } free(dir); } else { flexbuf_addchar(&cb, '\0'); ast = NewAST(AST_COMMENT, NULL, NULL); ast->d.string = flexbuf_get(&cb); comment_chain = AddToList(comment_chain, ast); } c = lexgetc(L); goto again; } if (L->eoln && (L->in_block == T_PUB || L->in_block == T_PRI)) { if (c == '\n') { c = lexgetc(L); goto again; } /* if there is a pending indent, send it back */ if (L->pending_indent) { lexungetc(L, c); --L->pending_indent; return T_INDENT; } /* on EOF send as many OUTDENTS as we need */ if (c == T_EOF) { if (L->indentsp > 0) { lexungetc(L, c); --L->indentsp; return T_OUTDENT; } } /* if our indentation is <= the start value, send back an outdent */ start_indent = L->colCounter-1; if (start_indent <= L->indent[L->indentsp] && L->indentsp > 0) { lexungetc(L, c); --L->indentsp; return T_OUTDENT; } } // force an end-of line at EOF if (c == T_EOF && !L->eoln && !L->eof) { L->eof = L->eoln = 1; return T_EOLN; } if (L->eoln) { L->eoln = 0; L->firstNonBlank = L->colCounter-1; } if (c == '\n') { L->eoln = 1; return T_EOLN; } if (current && !current->sawToken) { current->sawToken = 1; current->topcomment = GetComments(); } return c; }
/* parse an identifier */ static int parseIdentifier(LexStream *L, AST **ast_ptr, const char *prefix) { int c; struct flexbuf fb; Symbol *sym; AST *ast = NULL; int startColumn = L->colCounter - 1; char *idstr; flexbuf_init(&fb, INCSTR); if (prefix) { flexbuf_addmem(&fb, prefix, strlen(prefix)); if (gl_gas_dat) { flexbuf_addchar(&fb, '.'); } else { flexbuf_addchar(&fb, ':'); } } c = lexgetc(L); while (isIdentifierChar(c)) { //flexbuf_addchar(&fb, tolower(c)); flexbuf_addchar(&fb, c); c = lexgetc(L); } // add a trailing 0, and make sure there is room for an extra // character in case the name mangling needs it flexbuf_addchar(&fb, '\0'); flexbuf_addchar(&fb, '\0'); idstr = flexbuf_get(&fb); lexungetc(L, c); /* check for reserved words */ if (InDatBlock(L)) { sym = FindSymbol(&pasmWords, idstr); if (sym) { free(idstr); if (sym->type == SYM_INSTR) { ast = NewAST(AST_INSTR, NULL, NULL); ast->d.ptr = sym->val; *ast_ptr = ast; return T_INSTR; } if (sym->type == SYM_INSTRMODIFIER) { ast = NewAST(AST_INSTRMODIFIER, NULL, NULL); ast->d.ptr = sym->val; *ast_ptr = ast; return T_INSTRMODIFIER; } fprintf(stderr, "Internal error: Unknown pasm symbol type %d\n", sym->type); } } sym = FindSymbol(&reservedWords, idstr); if (sym != NULL) { if (sym->type == SYM_BUILTIN) { /* run any parse hooks */ Builtin *b = (Builtin *)sym->val; if (b && b->parsehook) { (*b->parsehook)(b); } goto is_identifier; } if (sym->type == SYM_CONSTANT || sym->type == SYM_FLOAT_CONSTANT) { goto is_identifier; } free(idstr); if (sym->type == SYM_RESERVED) { c = INTVAL(sym); /* check for special handling */ switch(c) { case T_PUB: case T_PRI: case T_DAT: case T_OBJ: case T_VAR: case T_CON: L->in_block = c; L->block_firstline = L->lineCounter; //EstablishIndent(L, 1); break; case T_ASM: if (L->in_block == T_ASM) { fprintf(stderr, "WARNING: ignoring nested asm\n"); } else { L->save_block = L->in_block; } L->in_block = c; break; case T_ENDASM: L->in_block = L->save_block; break; case T_IF: case T_IFNOT: case T_ELSE: case T_ELSEIF: case T_ELSEIFNOT: case T_REPEAT: case T_CASE: EstablishIndent(L, startColumn); break; default: break; } if (!ast) ast = GetComments(); *ast_ptr = ast; return c; } if (sym->type == SYM_HWREG) { ast = NewAST(AST_HWREG, NULL, NULL); ast->d.ptr = sym->val; *ast_ptr = ast; return T_HWREG; } fprintf(stderr, "Internal error: Unknown symbol type %d\n", sym->type); } is_identifier: ast = NewAST(AST_IDENTIFIER, NULL, NULL); /* make sure identifiers do not conflict with C keywords */ if (gl_normalizeIdents || Is_C_Reserved(idstr)) { NormalizeIdentifier(idstr); } ast->d.string = idstr; *ast_ptr = ast; return T_IDENTIFIER; }
void assembleInstruction(FILE *f, AST *ast) { uint32_t val, mask, src, dst; Instruction *instr; int i, numoperands, expectops; AST *operand[MAX_OPERANDS]; AST *line = ast; char *callname; AST *retast; instr = (Instruction *)ast->d.ptr; val = instr->binary; if (instr->ops != NOP_OPERANDS) { /* for anything except NOP set the condition to "always" */ val |= 0xf << 18; } /* check for modifiers and operands */ numoperands = 0; ast = ast->right; while (ast != NULL) { if (ast->kind == AST_EXPRLIST) { if (numoperands >= MAX_OPERANDS) { ERROR(line, "Too many operands to instruction"); return; } operand[numoperands++] = ast->left; } else if (ast->kind == AST_INSTRMODIFIER) { InstrModifier *mod = (InstrModifier *)ast->d.ptr; mask = mod->modifier; if (mask & 0x80000000) { val = val & mask; } else { val = val | mask; } } else { ERROR(line, "Internal error: expected instruction modifier found %d", ast->kind); return; } ast = ast->right; } /* parse operands and put them in place */ switch (instr->ops) { case NO_OPERANDS: case NOP_OPERANDS: expectops = 0; break; case TWO_OPERANDS: case JMPRET_OPERANDS: expectops = 2; break; default: expectops = 1; break; } if (expectops != numoperands) { ERROR(line, "Expected %d operands for %s, found %d", expectops, instr->name, numoperands); return; } src = dst = 0; switch (instr->ops) { case NO_OPERANDS: case NOP_OPERANDS: break; case TWO_OPERANDS: case JMPRET_OPERANDS: dst = EvalPasmExpr(operand[0]); src = EvalPasmExpr(operand[1]); break; case SRC_OPERAND_ONLY: dst = 0; src = EvalPasmExpr(operand[0]); break; case DST_OPERAND_ONLY: dst = EvalPasmExpr(operand[0]); src = 0; break; case CALL_OPERAND: if (operand[0]->kind != AST_IDENTIFIER) { ERROR(operand[0], "call operand must be an identifier"); return; } src = EvalPasmExpr(operand[0]); callname = malloc(strlen(operand[0]->d.string) + 8); strcpy(callname, operand[0]->d.string); strcat(callname, "_ret"); retast = NewAST(AST_IDENTIFIER, NULL, NULL); retast->d.string = callname; dst = EvalPasmExpr(retast); break; default: ERROR(line, "Unsupported instruction `%s'", instr->name); return; } if (src > 511) { ERROR(line, "Source operand too big for %s", instr->name); return; } if (dst > 511) { ERROR(line, "Destination operand too big for %s", instr->name); return; } val = val | (dst << 9) | src; /* output the instruction */ /* make sure it is aligned */ while ((datacount % 4) != 0) { outputByte(f, 0); } for (i = 0; i < 4; i++) { outputByte(f, val & 0xff); val = val >> 8; } }
void DeclareLabels(ParserState *P) { unsigned asmpc = 0; unsigned datoff = 0; unsigned inc = 0; unsigned delta; AST *ast = NULL; AST *pendingLabels = NULL; for (ast = P->datblock; ast; ast = ast->right) { switch (ast->kind) { case AST_BYTELIST: pendingLabels = emitPendingLabels(P, pendingLabels, datoff, asmpc, ast_type_byte); replaceHereDataList(ast->left, asmpc, 1); INCPC(dataListLen(ast->left, 1)); break; case AST_WORDLIST: ALIGNPC(2); pendingLabels = emitPendingLabels(P, pendingLabels, datoff, asmpc, ast_type_word); replaceHereDataList(ast->left, asmpc, 2); INCPC(dataListLen(ast->left, 2)); break; case AST_LONGLIST: ALIGNPC(4); pendingLabels = emitPendingLabels(P, pendingLabels, datoff, asmpc, ast_type_long); replaceHereDataList(ast->left, asmpc, 4); INCPC(dataListLen(ast->left, 4)); break; case AST_INSTRHOLDER: ALIGNPC(4); pendingLabels = emitPendingLabels(P, pendingLabels, datoff, asmpc, ast_type_long); replaceHeres(ast->left, asmpc/4); INCPC(4); break; case AST_IDENTIFIER: pendingLabels = AddToList(pendingLabels, NewAST(AST_LISTHOLDER, ast, NULL)); break; case AST_ORG: pendingLabels = emitPendingLabels(P, pendingLabels, datoff, asmpc, ast_type_long); if (ast->left) { replaceHeres(ast->left, asmpc/4); asmpc = 4*EvalPasmExpr(ast->left); } else { asmpc = 0; } break; case AST_RES: asmpc = align(asmpc, 4); pendingLabels = emitPendingLabels(P, pendingLabels, datoff, asmpc, ast_type_long); delta = EvalPasmExpr(ast->left); asmpc += 4*delta; break; case AST_FIT: asmpc = align(asmpc, 4); pendingLabels = emitPendingLabels(P, pendingLabels, datoff, asmpc, ast_type_long); if (ast->left) { int32_t max = EvalPasmExpr(ast->left); int32_t cur = (asmpc) / 4; if ( cur > max ) { ERROR(ast, "fit %d failed: pc is %d", max, cur); } } break; case AST_FILE: pendingLabels = emitPendingLabels(P, pendingLabels, datoff, asmpc, ast_type_byte); INCPC(filelen(ast->left)); break; default: ERROR(ast, "unknown element %d in data block", ast->kind); break; } } }