/* ParseReturn - parse the 'RETURN' statement */ static void ParseReturn(ParseContext *c) { Token tkn; if ((tkn = GetToken(c)) == T_EOL) { putcbyte(c, OP_LIT); putcword(c, 0); } else { SaveToken(c, tkn); ParseRValue(c); FRequire(c, T_EOL); } putcbyte(c, OP_RETURN); }
/* ParseGoto - parse the 'GOTO' statement */ static void ParseGoto(ParseContext *c) { FRequire(c, T_IDENTIFIER); putcbyte(c, OP_BR); putcword(c, ReferenceLabel(c, c->token, codeaddr(c))); FRequire(c, T_EOL); }
/* ParseDoUntil - parse the 'DO UNTIL' statement */ static void ParseDoUntil(ParseContext *c) { PushBlock(c); c->bptr->type = BLOCK_DO; c->bptr->u.DoBlock.nxt = codeaddr(c); ParseRValue(c); putcbyte(c, OP_BRT); c->bptr->u.DoBlock.end = putcword(c, 0); FRequire(c, T_EOL); }
/* ParseElseIf - parse the 'ELSE IF' statement */ static void ParseElseIf(ParseContext *c) { switch (CurrentBlockType(c)) { case BLOCK_IF: putcbyte(c, OP_BR); c->bptr->u.IfBlock.end = putcword(c, c->bptr->u.IfBlock.end); fixupbranch(c, c->bptr->u.IfBlock.nxt, codeaddr(c)); c->bptr->u.IfBlock.nxt = 0; ParseRValue(c); FRequire(c, T_THEN); putcbyte(c, OP_BRF); c->bptr->u.IfBlock.nxt = putcword(c, 0); FRequire(c, T_EOL); break; default: ParseError(c, "ELSE IF without a matching IF"); break; } }
/* StoreCode - store the function or method under construction */ void StoreCode(ParseContext *c) { int codeSize; /* check for unterminated blocks */ switch (CurrentBlockType(c)) { case BLOCK_IF: case BLOCK_ELSE: ParseError(c, "expecting END IF"); case BLOCK_FOR: ParseError(c, "expecting NEXT"); case BLOCK_DO: ParseError(c, "expecting LOOP"); case BLOCK_NONE: break; } /* fixup the RESERVE instruction at the start of the code */ if (c->codeType != CODE_TYPE_MAIN) { c->codeBuf[1] = c->localOffset; putcbyte(c, OP_RETURN); } /* make sure all referenced labels were defined */ CheckLabels(c); /* place string literals defined in this function */ PlaceStrings(c); /* determine the code size */ codeSize = (int)(c->cptr - c->codeBuf); #if 0 VM_printf("%s:\n", c->codeName); DecodeFunction((uint8_t *)c->image, (c->image->objectDataSize + GetObjSizeInWords(sizeof(VectorObjectHdr))) * sizeof(VMVALUE), c->codeBuf, codeSize); DumpSymbols(&c->arguments, "arguments"); DumpSymbols(&c->locals, "locals"); VM_printf("\n"); #endif /* store the vector object */ StoreBVectorData(c, c->code, PROTO_CODE, c->codeBuf, codeSize); /* empty the local heap */ c->nextLocal = c->sys->freeTop; InitSymbolTable(&c->arguments); InitSymbolTable(&c->locals); c->labels = NULL; /* reset to compile the next code */ c->codeType = CODE_TYPE_MAIN; c->cptr = c->codeBuf; }
/* CallHandler - compile a call to a runtime print function */ static void CallHandler(ParseContext *c, char *name, ParseTreeNode *expr) { Symbol *sym; /* find the built-in function */ if (!(sym = FindSymbol(&c->globals, name))) ParseError(c, "undefined print function: %s", name); /* compile the function symbol reference */ putcbyte(c, OP_LIT); putcword(c, sym->value); /* compile the argument */ if (expr) code_rvalue(c, expr); /* call the function */ putcbyte(c, OP_CALL); putcbyte(c, (expr ? 1 : 0)); putcbyte(c, OP_DROP); }
/* StartCode - start a function or method under construction */ void StartCode(ParseContext *c, CodeType type) { /* all methods must precede the main code */ if (type != CODE_TYPE_MAIN && c->codeFree > c->codeBuf) ParseError(c, "subroutines and functions must precede the main code"); /* don't allow nested functions or subroutines (for now anyway) */ if (type != CODE_TYPE_MAIN && c->codeType != CODE_TYPE_MAIN) ParseError(c, "nested subroutines and functions are not supported"); /* initialize the code object under construction */ InitSymbolTable(&c->arguments); InitSymbolTable(&c->locals); c->localOffset = 0; c->codeType = type; /* write the code prolog */ if (type != CODE_TYPE_MAIN) { putcbyte(c, OP_FRAME); putcbyte(c, 0); } }
/* ParseFor - parse the 'FOR' statement */ static void ParseFor(ParseContext *c) { ParseTreeNode *var, *step; int test, body, inst; Token tkn; PVAL pv; PushBlock(c); c->bptr->type = BLOCK_FOR; /* get the control variable */ FRequire(c, T_IDENTIFIER); var = GetSymbolRef(c, c->token); code_lvalue(c, var, &pv); FRequire(c, '='); /* parse the starting value expression */ ParseRValue(c); /* parse the TO expression and generate the loop termination test */ test = codeaddr(c); (*pv.fcn)(c, PV_STORE, &pv); (*pv.fcn)(c, PV_LOAD, &pv); FRequire(c, T_TO); ParseRValue(c); putcbyte(c, OP_LE); putcbyte(c, OP_BRT); body = putcword(c, 0); /* branch to the end if the termination test fails */ putcbyte(c, OP_BR); c->bptr->u.ForBlock.end = putcword(c, 0); /* update the for variable after an iteration of the loop */ c->bptr->u.ForBlock.nxt = codeaddr(c); (*pv.fcn)(c, PV_LOAD, &pv); /* get the STEP expression */ if ((tkn = GetToken(c)) == T_STEP) { step = ParseExpr(c); code_rvalue(c, step); tkn = GetToken(c); } /* no step so default to one */ else { putcbyte(c, OP_LIT); putcword(c, 1); } /* generate the increment code */ putcbyte(c, OP_ADD); inst = putcbyte(c, OP_BR); putcword(c, test - inst - 1 - sizeof(VMUVALUE)); /* branch to the loop body */ fixupbranch(c, body, codeaddr(c)); Require(c, tkn, T_EOL); }
/* ParseLoop - parse the 'LOOP' statement */ static void ParseLoop(ParseContext *c) { int inst; switch (CurrentBlockType(c)) { case BLOCK_DO: inst = putcbyte(c, OP_BR); putcword(c, c->bptr->u.DoBlock.nxt - inst - 1 - sizeof(VMUVALUE)); fixupbranch(c, c->bptr->u.DoBlock.end, codeaddr(c)); PopBlock(c); break; default: ParseError(c, "LOOP without a matching DO"); break; } FRequire(c, T_EOL); }
/* ParseElse - parse the 'ELSE' statement */ static void ParseElse(ParseContext *c) { int end; switch (CurrentBlockType(c)) { case BLOCK_IF: putcbyte(c, OP_BR); end = putcword(c, c->bptr->u.IfBlock.end); fixupbranch(c, c->bptr->u.IfBlock.nxt, codeaddr(c)); c->bptr->type = BLOCK_ELSE; c->bptr->u.ElseBlock.end = end; break; default: ParseError(c, "ELSE without a matching IF"); break; } FRequire(c, T_EOL); }
/* ParseIf - parse the 'IF' statement */ static void ParseIf(ParseContext *c) { Token tkn; ParseRValue(c); FRequire(c, T_THEN); PushBlock(c); c->bptr->type = BLOCK_IF; putcbyte(c, OP_BRF); c->bptr->u.IfBlock.nxt = putcword(c, 0); c->bptr->u.IfBlock.end = 0; if ((tkn = GetToken(c)) != T_EOL) { ParseStatement(c, tkn); fixupbranch(c, c->bptr->u.IfBlock.nxt, codeaddr(c)); PopBlock(c); } else Require(c, tkn, T_EOL); }
/* ParseImpliedLetOrFunctionCall - parse an implied let statement or a function call */ static void ParseImpliedLetOrFunctionCall(ParseContext *c) { ParseTreeNode *expr; Token tkn; PVAL pv; expr = ParsePrimary(c); switch (tkn = GetToken(c)) { case '=': code_lvalue(c, expr, &pv); ParseRValue(c); (*pv.fcn)(c, PV_STORE, &pv); break; default: SaveToken(c, tkn); code_rvalue(c, expr); putcbyte(c, OP_DROP); break; } FRequire(c, T_EOL); }
/* ParseNext - parse the 'NEXT' statement */ static void ParseNext(ParseContext *c) { ParseTreeNode *var; int inst; switch (CurrentBlockType(c)) { case BLOCK_FOR: FRequire(c, T_IDENTIFIER); var = GetSymbolRef(c, c->token); /* BUG: check to make sure it matches the symbol used in the FOR */ inst = putcbyte(c, OP_BR); putcword(c, c->bptr->u.ForBlock.nxt - inst - 1 - sizeof(VMUVALUE)); fixupbranch(c, c->bptr->u.ForBlock.end, codeaddr(c)); PopBlock(c); break; default: ParseError(c, "NEXT without a matching FOR"); break; } FRequire(c, T_EOL); }
/* Compile - compile a program */ int Compile(ParseContext *c, uint8_t *imageSpace, size_t imageSize, size_t textMax, size_t dataMax) { ImageHdr *image = (ImageHdr *)imageSpace; VMUVALUE textSize; /* setup an error target */ if (setjmp(c->errorTarget) != 0) return -1; /* initialize the image */ if (imageSize < sizeof(ImageHdr) + textMax + dataMax) return -1; memset(image, 0, sizeof(ImageHdr)); c->image = image; /* empty the heap */ c->localFree = c->heapBase; c->globalFree = c->heapTop; /* initialize the image */ c->textBase = c->textFree = imageSpace + sizeof(ImageHdr); c->textTop = c->textBase + textMax; c->dataBase = c->dataFree = c->textBase + textMax; c->dataTop = c->dataBase + dataMax; /* initialize the code buffer */ c->codeFree = c->codeBuf; c->codeTop = c->codeBuf + sizeof(c->codeBuf); /* initialize block nesting table */ c->btop = (Block *)((char *)c->blockBuf + sizeof(c->blockBuf)); c->bptr = c->blockBuf - 1; /* initialize the global symbol table and string table */ InitSymbolTable(&c->globals); /* enter the built-in functions */ EnterBuiltInFunction(c, "delayMs", bi_delayms, sizeof(bi_delayms)); EnterBuiltInFunction(c, "updateLeds", bi_updateleds, sizeof(bi_updateleds)); /* enter the built-in variables */ /* typedef struct { int32_t triggerTop; int32_t triggerBottom; int32_t numLeds; int32_t led[RGB_SIZE]; int32_t patternnum; } VM_variables; */ EnterBuiltInVariable(c, "triggerTop", sizeof(VMVALUE)); EnterBuiltInVariable(c, "triggerBottom", sizeof(VMVALUE)); EnterBuiltInVariable(c, "numLeds", sizeof(VMVALUE)); EnterBuiltInVariable(c, "led", sizeof(VMVALUE) * RGB_SIZE); EnterBuiltInVariable(c, "patternNum", sizeof(VMVALUE)); /* initialize the string table */ c->strings = NULL; /* initialize the label table */ c->labels = NULL; /* start in the main code */ c->codeType = CODE_TYPE_MAIN; /* initialize scanner */ c->inComment = VMFALSE; c->lineNumber = 0; /* compile each line */ while (GetLine(c)) { int tkn; if ((tkn = GetToken(c)) != T_EOL) ParseStatement(c, tkn); } /* end the main code with a halt */ putcbyte(c, OP_HALT); /* write the main code */ StartCode(c, CODE_TYPE_MAIN); image->entry = StoreCode(c); /* determine the text size */ textSize = c->textFree - c->textBase; /* fill in the image header */ image->dataOffset = sizeof(ImageHdr) + textSize; image->dataSize = c->dataFree - c->dataBase; image->imageSize = image->dataOffset + image->dataSize; /* make the data contiguous with the code */ memcpy(&imageSpace[image->dataOffset], c->dataBase, image->dataSize); #ifdef COMPILER_DEBUG VM_printf("entry "); PrintValue(image->entry); VM_printf("\n"); VM_printf("imageSize "); PrintValue(image->imageSize); VM_printf("\n"); VM_printf("textSize "); PrintValue(textSize); VM_printf("\n"); VM_printf("dataOffset "); PrintValue(image->dataOffset); VM_printf("\n"); VM_printf("dataSize "); PrintValue(image->dataSize); VM_printf("\n"); DumpSymbols(&c->globals, "symbols"); #endif /* return successfully */ return 0; }
/* Compile - compile a program */ ImageHdr *Compile(ParseContext *c) { System *sys = c->sys; ImageHdr *image = c->image; size_t maxHeapUsed = c->maxHeapUsed; VMVALUE *variables; VMUVALUE totalSize; Symbol *sym; /* setup an error target */ if (setjmp(c->errorTarget) != 0) return NULL; /* initialize the scratch buffer */ BufRewind(); /* allocate space for the object table */ image->objectCount = 0; image->objectDataSize = 0; /* use the rest of the free space for the compiler heap */ c->nextGlobal = sys->freeNext; c->nextLocal = sys->freeTop; c->heapSize = sys->freeTop - sys->freeNext; c->maxHeapUsed = 0; /* initialize the global variable count */ image->variableCount = 0; /* initialize block nesting table */ c->btop = (Block *)((char *)c->blockBuf + sizeof(c->blockBuf)); c->bptr = c->blockBuf - 1; /* initialize the code staging buffer */ c->ctop = c->codeBuf + sizeof(c->codeBuf); c->cptr = c->codeBuf; /* initialize the string and label tables */ c->strings = NULL; c->labels = NULL; /* start in the main code */ c->codeType = CODE_TYPE_MAIN; /* initialize the global symbol table */ InitSymbolTable(&c->globals); /* add the intrinsic functions */ AddIntrinsic(c, "ABS", FN_ABS); AddIntrinsic(c, "RND", FN_RND); AddIntrinsic(c, "printStr", FN_printStr); AddIntrinsic(c, "printInt", FN_printInt); AddIntrinsic(c, "printTab", FN_printTab); AddIntrinsic(c, "printNL", FN_printNL); AddIntrinsic(c, "printFlush", FN_printFlush); #ifdef PROPELLER AddIntrinsic(c, "IN", FN_IN); AddIntrinsic(c, "OUT", FN_OUT); AddIntrinsic(c, "HIGH", FN_HIGH); AddIntrinsic(c, "LOW", FN_LOW); AddIntrinsic(c, "TOGGLE", FN_TOGGLE); AddIntrinsic(c, "DIR", FN_DIR); AddIntrinsic(c, "GETDIR", FN_GETDIR); AddIntrinsic(c, "CNT", FN_CNT); AddIntrinsic(c, "PAUSE", FN_PAUSE); AddIntrinsic(c, "PULSEIN", FN_PULSEIN); AddIntrinsic(c, "PULSEOUT", FN_PULSEOUT); #endif /* initialize scanner */ c->inComment = VMFALSE; /* get the next line */ while (GetLine(c)) { Token tkn; if ((tkn = GetToken(c)) != T_EOL) ParseStatement(c, tkn); } /* end the main code with a halt */ putcbyte(c, OP_HALT); /* write the main code */ StartCode(c, "main", CODE_TYPE_MAIN); image->mainCode = c->code; StoreCode(c); /* allocate the global variable table */ if (!(variables = (VMVALUE *)AllocateFreeSpace(sys, image->variableCount * sizeof(VMVALUE)))) ParseError(c, "insufficient space for variable table"); /* store the initial values of the global variables */ for (sym = c->globals.head; sym != NULL; sym = sym->next) { if (!(sym->value & INTRINSIC_FLAG)) variables[sym->value] = sym->initialValue; } /* write out the variable and object tables */ if (!BufWriteWords(variables, image->variableCount) || !BufWriteWords(image->objects, image->objectCount)) ParseError(c, "insufficient scratch space"); /* free up the space the compiler was consuming */ sys->freeNext = c->freeMark; /* allocate space for the object data and variables */ totalSize = (image->objectDataSize + image->variableCount + image->objectCount) * sizeof(VMVALUE); if (!(image->objectData = (VMVALUE *)AllocateFreeSpace(sys, totalSize))) ParseError(c, "insufficient space for objects and variables"); image->variables = image->objectData + image->objectDataSize; image->objects = image->variables + image->variableCount; /* read the object data and variables from the scratch buffer */ BufRewind(); if (!BufReadWords(image->objectData, totalSize)) ParseError(c, "error reading objects and variables"); { int objectTableSize = image->objectCount * sizeof(VMVALUE); int objectDataSize = image->objectDataSize * sizeof(VMVALUE); int dataSize = objectTableSize + objectDataSize + image->variableCount * sizeof(VMVALUE); #if 0 DumpSymbols(&c->globals, "symbols"); #endif VM_printf("H:%d", maxHeapUsed); VM_printf(" O:%d", image->objectCount); VM_printf(" D:%d", objectDataSize); VM_printf(" V:%d", image->variableCount); VM_printf(" T:%d\n", dataSize); } /* return the image */ return image; }
/* ParseStop - parse the 'STOP' statement */ static void ParseStop(ParseContext *c) { putcbyte(c, OP_HALT); FRequire(c, T_EOL); }
/* StoreCode - store the function or method under construction */ VMVALUE StoreCode(ParseContext *c) { size_t codeSize; VMVALUE code; uint8_t *p; /* check for unterminated blocks */ switch (CurrentBlockType(c)) { case BLOCK_IF: case BLOCK_ELSE: ParseError(c, "expecting END IF"); case BLOCK_FOR: ParseError(c, "expecting NEXT"); case BLOCK_DO: ParseError(c, "expecting LOOP"); case BLOCK_NONE: break; } /* fixup the RESERVE instruction at the start of the code */ if (c->codeType != CODE_TYPE_MAIN) { c->codeBuf[1] = 2 + c->localOffset; putcbyte(c, OP_RETURN); } /* make sure all referenced labels were defined */ CheckLabels(c); /* allocate code space */ codeSize = (int)(c->codeFree - c->codeBuf); p = (uint8_t *)ImageTextAlloc(c, codeSize); memcpy(p, c->codeBuf, codeSize); /* get the address of the compiled code */ code = (VMVALUE)(p - (uint8_t *)c->image); #ifdef COMPILER_DEBUG { VM_printf("%s:\n", c->codeSymbol ? c->codeSymbol->name : "<main>"); DecodeFunction((uint8_t *)c->image, (uint8_t *)c->image + code, codeSize); DumpSymbols(&c->arguments, "arguments"); DumpSymbols(&c->locals, "locals"); VM_printf("\n"); } #endif /* prepare the buffer for the next function */ c->codeFree = c->codeBuf; /* empty the local heap */ c->localFree = c->heapBase; InitSymbolTable(&c->arguments); InitSymbolTable(&c->locals); c->labels = NULL; /* reset to compile the next code */ c->codeType = CODE_TYPE_MAIN; /* return the code vector */ return code; }