/* | CALC command for Epsilon | | (C)CopyRight Yuval Rakavy 1986 All Rights reserved. | | Version 1.1 - Character constants added | | This file contains a pocket calculator package for Epsilon | | Full C expression are supported execpt for assigment as an operator, | and the ? : construct. | | The following commands are defined: | | calc Evaluate the value of an expression | calc prompts you for an expression having the following syntax: | A) = | or | B) | | Where is a valid C language expression. You may assign | values to variables by using syntax A:. Variables are defined when thay | are first used. A newly defined variable has the value 0. | The result is displayed in decimal and hex in the echo area, and also | assigned to the variable '_'. | | set-result-format Define the format used by the insert-result command | set-result-format prompts you for a 'printf' like format to be used | when inserting the result obtained by the last calc command into the | current buffer. | | insert-result Insert last result obtained from calc into the buffer | insert-result inserts the result calculated by the last invokation of the | calc command into the buffer. The format of the inserted text is | controlled by the set-result-format command. | | dump-variables Display the names and values of all calc variables | dump-variables displayes a list of all the CALC variables defined. | for each variable defined, dump-variables displayed the name of | the variable and its current value in both hex and decimal. | | set-iter-from-calc Set interation count value to the last 'calc' result. */ #define NULL 0 #define TOKEN_NUMBER 0x80 #define TOKEN_NAME 0x81 #define TOKEN_LSHIFT 0x82 #define TOKEN_RSHIFT 0x83 #define TOKEN_EQ 0x84 #define TOKEN_NOTEQ 0x85 #define TOKEN_LAND 0x86 #define TOKEN_LOR 0x87 #define TOKEN_LEQ 0x88 #define TOKEN_GEQ 0x89 #define TOKEN_BASE 'B' #define TOKEN_INC 0x8a #define TOKEN_DEC 0x8b #define TOKEN_VARIABLE 0x8c struct CalcName { char *Name; /* Variable name */ int Value; /* Variable value */ int *NextVar; /* BUG in EEL prevent the correct type which is: | struct CalcName *NextVar */ }; struct { char First; char Second; int Token; } CalcDoubleCharToken[] = { '>', '>', TOKEN_RSHIFT, '<', '<', TOKEN_LSHIFT, '=', '=', TOKEN_EQ, '!', '=', TOKEN_NOTEQ, '&', '&', TOKEN_LAND, '|', '|', TOKEN_LOR, '<', '=', TOKEN_LEQ, '>', '=', TOKEN_GEQ, '+', '+', TOKEN_INC, '-', '-', TOKEN_DEC, 0, 0, 0 }; int CalcResult; char CalcSyntaxError; char *CalcNext; char CalcDigits[] = "0123456789ABCDEF"; char CalcPushBackToken = 0; char CalcFormat[20] = "%d"; int CalcParanLevel; struct CalcName *CalcVariables = NULL; struct CalcName *CalcFindVar(); union { int Numeric; struct CalcName *Variable; } CalcLLval; struct CalcPrior { char Token; char Priority; } CalcPrior[] = { 0, 0, TOKEN_BASE, 1, ')', 1, TOKEN_LOR, 2, TOKEN_LAND, 3, '|', 4, '^', 5, '&', 6, TOKEN_EQ, 7, TOKEN_NOTEQ, 7, '<', 8, '>', 8, TOKEN_LEQ, 8, TOKEN_GEQ, 8, TOKEN_RSHIFT, 9, TOKEN_LSHIFT, 9, '+', 10, '-', 10, '*', 11, '/', 11, '%', 11, 0xff, 0 }; struct CalcName *CalcLastResult = NULL; command calc() { char Expr[80]; int TmpCalcResult; get_string(Expr, "Expression: "); CalcSyntaxError = 0; CalcParanLevel = 0; TmpCalcResult = CalcEval(Expr); if(CalcParanLevel != 0) { if(CalcParanLevel < 0) error("Too many )"); else error("Missing )"); } else if(!CalcSyntaxError) { CalcResult = TmpCalcResult; say("Result: %d %xH", CalcResult, CalcResult); if(CalcLastResult == NULL) CalcLastResult = CalcFindVar("_"); CalcLastResult->Value = CalcResult; } } command set_result_format() { char Buf[30]; sprintf(Buf, "Calc format [%s]: ", CalcFormat); get_string(Buf, Buf); if(Buf[0] != '\0') strcpy(CalcFormat, Buf); } command insert_result() { bprintf(CalcFormat, CalcResult); } command dump_variables() { char *OldBuf = bufname; struct CalcName *p; char *temp_buf(); bufname = temp_buf(); bprintf("Calc variables dump:\n\n"); bprintf("Name: Value\n"); for(p = CalcVariables; p; p = (struct CalcName *)p->NextVar) bprintf("%-12s %5d %5x\n", p->Name, p->Value, p->Value); view_buffer(bufname); bufname = OldBuf; } command set_iter_from_calc() { iter = CalcResult; getkey(); has_arg = 1; say(""); do_topkey(); has_arg = 0; iter = 1; } CalcSkip() { while(*CalcNext == ' ') CalcNext++; } IsNumberCharacter(c) { return ('0' <= c && c <= '9') || isalpha(c); } IsSymbolCharacter(c) { c = tolower(c); return ('a' <= c && c <= 'z') || c == '_' || IsNumberCharacter(c); } /* | Subroutines used by the calc routines */ struct CalcName *CalcFindVar(Name) char *Name; { struct CalcName *p; for(p = CalcVariables; p; p = (struct CalcName *)p->NextVar) if(!strcmp(p->Name, Name)) return p; p = (struct CalcName *)malloc(sizeof(struct CalcName)); p->Name = (char *)strsave(Name); p->NextVar = (int *)CalcVariables; p->Value = 0; CalcVariables = p; return p; } CalcToken() { char c, *p; char Buf[20]; int Radix, Index; if(CalcPushBackToken) { Index = CalcPushBackToken; CalcPushBackToken = 0; return Index; } CalcSkip(); if('0' <= *CalcNext && *CalcNext <= '9') { /* | Number, collect all alpha-numeric characters into buffer | then try to figure out the base */ Radix = -1; if(*CalcNext == '0' && toupper(CalcNext[1]) == 'X') { Radix = 16; /* C Style hex number '0xa' */ CalcNext += 2; } for(Index = 0; Index< 10 && *CalcNext && IsNumberCharacter(*CalcNext); Index++) Buf[Index] = *CalcNext++; Buf[Index] = '\0'; if(Radix == -1) { /* Figure out the radix based on the last character */ Radix = 10; switch(toupper(Buf[Index-1])) { case 'H': Radix = 16; break; case 'O': case 'Q': Radix = 8; break; case 'B': Radix = 2; break; } if(Radix != 10) Buf[Index-1] = '\0'; else if(Buf[0] == '0') Radix = 8; /* C like octal numbers */ } CalcLLval.Numeric = 0; for(Index = 0; Buf[Index]; Index++) { if(!(p = index(CalcDigits, toupper(Buf[Index])))) { error("Invalid digit"); return 0; } if((p - CalcDigits) >= Radix) { error("Invalid digit %c for radix %d", Buf[Index], Radix); return 0; } else CalcLLval.Numeric = CalcLLval.Numeric*Radix + (p-CalcDigits); } return TOKEN_NUMBER; } else if(*CalcNext == '\'') { CalcLLval.Numeric = *++CalcNext; if(*++CalcNext == '\'') CalcNext++; /* Skip on optional closeing ' */ return TOKEN_NUMBER; } else if(('a' <= tolower(*CalcNext) && tolower(*CalcNext) <= 'z') || *CalcNext == '_') { /* We have a name */ for(p = Buf; IsSymbolCharacter(*CalcNext); p++) *p = *CalcNext++; *p = '\0'; CalcLLval.Variable = CalcFindVar(Buf); return TOKEN_VARIABLE; } else { if(*CalcNext == '\0') return 0; /* | Check if we have a double character token */ for(Index = 0; CalcDoubleCharToken[Index].First != 0; Index++) if(*CalcNext == CalcDoubleCharToken[Index].First && CalcNext[1] == CalcDoubleCharToken[Index].Second) { CalcNext += 2; return CalcDoubleCharToken[Index].Token; } if(*CalcNext) return *CalcNext++; else return 0; } } CalcFindPrior(Operator) { int i; for(i = 0; CalcPrior[i].Token != 0xff; i++) if(CalcPrior[i].Token == Operator) return CalcPrior[i].Priority; CalcSyntaxError = 1; error("Unkown operator in expression (%x)", Operator); return 0; } CalcUnaryExp() { int Token; int Value; Token = CalcToken(); if(Token == TOKEN_NUMBER) return CalcLLval.Numeric; else if(Token == TOKEN_VARIABLE) { Token = CalcToken(); if(Token == TOKEN_INC || Token == TOKEN_DEC) { Value = CalcLLval.Variable->Value; if(Token == TOKEN_INC) CalcLLval.Variable->Value++; else CalcLLval.Variable->Value--; return Value; } else CalcPushBackToken = Token; return CalcLLval.Variable->Value; } else if(Token == '-') return -CalcUnaryExp(); else if(Token == '~') return ~CalcUnaryExp(); else if(Token == '(') { CalcParanLevel++; CalcPushBackToken = TOKEN_BASE; return CalcDoEval(0); } else if(Token == '!') return !CalcUnaryExp(); else if(Token == TOKEN_INC || Token == TOKEN_DEC) { if(CalcToken() != TOKEN_VARIABLE) { CalcSyntaxError = 1; error("Variable expected after ++ or --"); CalcLLval.Numeric = 0; return TOKEN_NUMBER; } if(Token == TOKEN_INC) CalcLLval.Variable->Value++; else CalcLLval.Variable->Value--; return CalcLLval.Variable->Value; } if(!CalcSyntaxError) { CalcSyntaxError = 1; error("Number expected %d", Token); } CalcLLval.Numeric = 0; return TOKEN_NUMBER; } CalcDoEval(Left) { int Oper1; int Pri1; int Oper2, Pri2; int Right; while((Oper1 = CalcToken()) != 0 && Oper1 != ')') { Pri1 = CalcFindPrior(Oper1); if(*CalcNext) { Right = CalcUnaryExp(); Oper2 = CalcToken(); Pri2 = CalcFindPrior(Oper2); CalcPushBackToken = Oper2; } if(Pri1 < Pri2) Right = CalcDoEval(Right); switch(Oper1) { case '+': Left += Right; break; case '-': Left -= Right; break; case '*': Left *= Right; break; case '/': Left /= Right; break; case '%': Left %= Right; break; case TOKEN_LOR: Left = Left || Right; break; case TOKEN_LAND: Left = Left && Right; break; case '|': Left |= Right; break; case '&': Left &= Right; break; case '^': Left ^= Right; break; case TOKEN_EQ: Left = Left == Right; break; case TOKEN_NOTEQ: Left = Left != Right; break; case '<': Left = Left < Right; break; case '>': Left = Left > Right; break; case TOKEN_LEQ: Left = Left <= Right; break; case TOKEN_GEQ: Left = Left >= Right; break; case TOKEN_LSHIFT: Left <<= Right; break; case TOKEN_RSHIFT: Left >>= Right; break; case TOKEN_BASE: Left = Right; break; } if(Pri2 < Pri1) return Left; } if(Oper1 == ')') CalcParanLevel--; return Left; } CalcEval(Expr) char *Expr; { int Token; struct CalcName *p; /* | Test for special case of assignment statement */ CalcNext = Expr; CalcParanLevel = 0; CalcPushBackToken = 0; if(CalcToken() == TOKEN_VARIABLE) { if(CalcToken() == '=') { /* Assignment statment */ p = CalcLLval.Variable; CalcPushBackToken = TOKEN_BASE; p->Value = CalcDoEval(0); return p->Value; } } CalcPushBackToken = TOKEN_BASE; CalcNext = Expr; return CalcDoEval(0); }