C Programming Column by Al Stevens Listing One #include #include "interp.h" // ----- intrinsic functions int iprntf(int* p) // printf { printf(reinterpret_cast(p[0]),p[1],p[2],p[3],p[4]); return 0; } int igtch() // getchar { return getchar(); } int iptch(int* c) // putchar { return putchar(*c); } char* getver() // return a string { return "Version 1.0"; } Intrinsic funcs[] = { Intrinsic("int printf", reinterpret_cast(iprntf)), Intrinsic("int getchar", reinterpret_cast(igtch) ), Intrinsic("int putchar", reinterpret_cast(iptch) ), Intrinsic("string getversion",reinterpret_cast(getver)), Intrinsic("", 0) }; // ---------- error messages char *erm[]={ "Unexpected end of file", "Unrecognized", "Duplicate ident", "Undeclared ident", "Syntax Error", "Unmatched {}", "Unmatched ()", "Missing", "Not a function", "Misplaced break", "Out of place", "Not an identifer", "Mismatched arguments", "Divide by zero", "Invalid constant", "No main function" }; static FILE *fp; int main(int argc, char *argv[]) { if (argc == 2) { if ((fp = fopen(argv[1], "r")) != 0) { try { SInterpreter si(funcs); si.interpret(); } catch (SIException sex) { printf("\n%s %s on line %d\n",erm[sex.ercode], sex.msg.c_str(), sex.lineno); } fclose(fp); } } return 0; } // ----- functions that the interpreter requires int getsource(void) { return getc(fp); } void ungetsource(int c) { ungetc(c, fp); } Listing Two // ---------------- interp.h -------------------- #include #include // namespace DDJScriptInterpreter { // ----------- error codes enum errs { EARLYEOF, UNRECOGNIZED, DUPL_DECLARE, UNDECLARED, SYNTAX, BRACERR, PARENERR, MISSING, NOTFUNC, BREAKERR, OUTOFPLACE, NOTIDENT, MISMATCHEDARG, DIVIDEERR, INVALIDCONSTANT, NOMAIN }; class SIException { public: errs ercode; int lineno; std::string msg; SIException(errs er = SYNTAX, int lno = 0, std::string m = std::string()) : ercode(er), lineno(lno), msg(m) { } }; typedef int(*ifunc)(void*); // --- intrinsic function table (provided by shell application) class Intrinsic { public: std::string signature; ifunc fn; Intrinsic(const std::string& sig = std::string(), ifunc f = 0) : signature(sig), fn(f) { } }; typedef short int token; enum DatumType { unknown, number, strng }; #define UNARY(op) \ Datum operator op () const \ { \ nostring(); \ return Datum(op value); \ } #define RELATIONAL(op) \ bool operator op (const Datum& d) const \ { \ sametype(d); \ return (type == strng) ? (strval op d.strval) : (value op d.value); \ } #define ARITHMETIC(op) \ Datum operator op (const Datum& d) const \ { \ nostring(); \ d.nostring(); \ return Datum(value op d.value); \ } #define LOGICAL(op) \ bool operator op (const Datum& d) const \ { \ nostring(); \ d.nostring(); \ return value op d.value; \ } class Datum { void nostring() const { if (type == strng) throw SIException(); } void sametype(const Datum& d) const { if (type != d.type) throw SIException(); } public: DatumType type; int value; // number value std::string strval; // string value Datum() : type(unknown), value(0) { } explicit Datum(int val) : type(number), value(val) { } explicit Datum(std::string str) : type(strng), value(0), strval(str) { } Datum& operator=(const Datum& d) { type = d.type; value = d.value; strval = d.strval; return *this; } Datum operator+(const Datum& d) const { sametype(d); if (type == strng) return Datum(strval + d.strval); // concatenate strings return Datum(value + d.value); // sum numbers } bool operator!() const { nostring(); return !value; } UNARY(-) ARITHMETIC(*) ARITHMETIC(/) ARITHMETIC(-) RELATIONAL(<=) RELATIONAL(>=) RELATIONAL(!=) RELATIONAL(==) RELATIONAL(<) RELATIONAL(>) LOGICAL(&&) LOGICAL(||) }; class Token { public: token tok; Datum datum; int tokennumber; Token(token t = 0) : tok(t) { } bool operator<(const Token& t) const { return tok < t.tok; } bool operator==(const Token& t) const { return tok == t.tok; } Token& operator=(const Token& t) { tok = t.tok; datum = t.datum; return *this; } }; typedef std::vector token_buffer; typedef token_buffer::iterator token_iter; enum SymbolType { none, variable, ifunction, pfunction }; class Symbol { public: SymbolType type; std::string name; Datum datum; int entry; // subscript to function's first entry in token buffer ifunc fn; // points to intrinsic function Symbol(SymbolType ty = none, const std::string nm = std::string() ) : type(ty), name(nm), entry(0), fn(0) { } bool operator<(const Symbol& s) const { return name < s.name; } bool operator==(const Symbol& s) const { return name == s.name; } Symbol& operator=(const Symbol& s) { type = s.type; name = s.name; datum = s.datum; entry = s.entry; fn = s.fn; return *this; } }; typedef std::vector symbol_table; typedef symbol_table::iterator symbol_iter; class SInterpreter { token_iter tokiter; // iterates the token buffer during interpreting private: class Keyword { public: std::string kw; Token kwtoken; Keyword(const char* k, Token tk) : kw(k), kwtoken(tk) { } }; symbol_table symboltable; token_buffer tokens; int currentscope; // index of first symbol table entry for current scope static token tokentbl[]; static Keyword keywords[]; Datum frtn; // return value from a function bool breaking, returning; int skipping; int linenumber; bool scanned; // true when lexical scan is complete int LineNumber(); // current source file line number void initialize(); // initialize data variables // functions for lexical scan void lexicalscan(); bool declarator(bool islocal, bool isparameter = false); void declarators(bool islocal); Token compilenextsourcetoken(); int escseq(); int getsourcechar(); int getrawsourcechar(); // functions for compiling and interpreting program Token nexttoken(); void prevtoken(); Token needtoken(token tkn); Datum function(Symbol sym); bool findsymbol(int& ndx, const std::string& name, int fromscope = 0); void compound_statement(int scope); void statement(); void outofscope(); void statements(); void skip_statements(); bool istoken(token tkn); void skippair(token ltkn, token rtkn); Datum primary(); Datum mult(); Datum plus(); Datum le(); Datum eq(); Datum and(); Datum expression(); bool isidentchar(int c) { return isalpha(c) || isdigit(c) || c == '_'; } bool iswhite(int c) { return c == ' ' || c == '\t'; } public: explicit SInterpreter(const Intrinsic* inf); int interpret(); }; // } // namespace DDJScriptInterpreter // ------ functions provided by the shell int getsource(); void ungetsource(int ch); 5