writing the machine parser
[antikythera] / src / mparser.cc
index b39df1a..90c1b6d 100644 (file)
@@ -1,47 +1,55 @@
-#include <vector>
+#include <string>
+#include <map>
+#include <stack>
 #include "mparser.h"
 #include "machine.h"
 
-enum ASTNodeType {
-       AST_MACHINE,
-       AST_GEAR,
-       AST_STATEMENT,
-       AST_IDENT,
-       AST_NUMBER,
-       AST_VECTOR,
-       AST_FCALL
-};
-
-struct ASTNode {
-       std::vector<ASTNode*> children;
+enum ValueType { VAL_NUM, VAL_VEC, VAL_STR };
+struct Value {
+       ValueType type;
+       char *str;
+       float v[4];
 };
 
 struct ParserState {
        FILE *fp;
        const char *fname;
+       int nline, nerrors;
        Machine *mcn;
 
        int nextc;
        int savedc;
 
-       bool error;
+       std::stack<Value> val;
+       std::map<std::string, Value> var;
+       std::string last_mod_var;
+
+       std::map<std::string, Gear*> gears;
 };
 
+#define MAX_ID_LEN     31
+
 static bool machine(ParserState *ps);
 static bool expect(ParserState *ps, char c);
-static bool expect_alpha(ParserState *ps);
-static bool expect_digit(ParserState *ps);
-static bool expect_alnum(ParserState *ps);
-static bool match_str(ParserState *ps, const char *str);
-static bool match_char(ParserState *ps, char c);
-static bool mstatement(ParserState *ps);
+static bool statement(ParserState *ps);
 static bool ident(ParserState *ps, std::string *s);
 static bool gear(ParserState *ps);
 static bool expression(ParserState *ps);
 
+static char *get_ident(ParserState *ps, char *buf, int bsz);
+
 static bool nextchar(ParserState *ps);
 static void putback(ParserState *ps, char c);
 
+static void expected(ParserState *ps, const char *str);
+
+static void set_var(ParserState *ps, const char *name, const Value &value);
+static bool have_var(ParserState *ps, const char *name);
+static const Value &get_var(ParserState *ps, const char *name);
+
+static void update_gear_vars(ParserState *ps, Gear *gear);
+static bool set_gear_var(Gear *gear, const char *name, const Value &val);
+
 bool parse_machine(Machine *mcn, const char *fname)
 {
        FILE *fp = fopen(fname, "rb");
@@ -49,11 +57,12 @@ bool parse_machine(Machine *mcn, const char *fname)
 
        ParserState pstate;
        pstate.fname = fname;
+       pstate.nline = 1;
+       pstate.nerrors = 0;
        pstate.fp = fp;
        pstate.mcn = mcn;
-       pstate.savedc = -1;
        pstate.nextc = fgetc(fp);       // prime the parser
-       pstate.error = false;
+       pstate.savedc = -1;
 
        bool res = machine(&pstate);
        fclose(fp);
@@ -62,11 +71,18 @@ bool parse_machine(Machine *mcn, const char *fname)
 
 static bool machine(ParserState *ps)
 {
-       matchstr(ps, "machine");
+       char tok[MAX_ID_LEN + 1];
+       if(strcmp(get_ident(ps, tok, sizeof tok), "machine") != 0) {
+               expected(ps, "machine");
+               return false;
+       }
        expect(ps, '{');
 
-       while(mstatement(ps));
-
+       while(ps->nextc != '}') {
+               if(!statement(ps)) {
+                       return false;
+               }
+       }
        expect(ps, '}');
        return true;
 }
@@ -74,87 +90,79 @@ static bool machine(ParserState *ps)
 static bool expect(ParserState *ps, int c)
 {
        if(c != ps->nextc) {
-               ps->error = true;
+               char buf[2] = {0, 0};
+               buf[0] = c;
+               expected(ps, buf);
                return false;
        }
        nextchar(ps);
        return true;
 }
 
-static bool expect_alpha(ParserState *ps)
+static bool statement(ParserState *ps)
 {
-       if(!isalpha(ps->nextc)) {
-               ps->error = true;
+       char id[MAX_ID_LEN + 1];
+       if(!get_ident(ps, id, sizeof id)) {
                return false;
        }
-       nextchar(ps);
-       return true;
-}
 
-static bool expect_digit(ParserState *ps)
-{
-       if(!isdigit(ps->nextc)) {
-               ps->error = true;
-               return false;
-       }
-       nextchar(ps);
-       return true;
-}
+       if(strcmp(id, "gear") == 0) {
+               if(!get_ident(ps, id, sizeof id)) {
+                       fprintf(stderr, "gear name missing\n");
+                       return false;
+               }
+               expect(ps, '{');
 
-static bool expect_alnum(ParserState *ps)
-{
-       if(!isalnum(ps->nextc)) {
-               ps->error = true;
-               return false;
-       }
-       nextchar(ps);
-       return true;
-}
+               Gear *gear = new Gear;
+               gear->name = id;
+               ps->gears[id] = gear;
+               update_gear_vars(ps, gear);
 
-static bool match_str(ParserState *ps, const char *str)
-{
-       while(*str && match_char(ps, *str++));
-       return !feof(ps->fp);
-}
+               while(ps->nextc != '}') {
+                       if(!statement(ps)) {
+                               return false;
+                       }
+                       update_gear_vars(ps, gear);
+               }
+               expect(ps, '}');
 
-static bool match_char(ParserState *ps, int c)
-{
-       if(ps->nextc != c) {
-               return false;
+               ps->mcn->add_gear(gear);
        }
-       nextchar(ps);
-       return true;
-}
-
-static bool expression(ParserState *ps);
-
-#define CHECK(x) if(!(x)) return false
 
-static bool mstatement(ParserState *ps)
-{
-       std::string id;
-
-       CHECK(ident(ps, &id));
-       if(id == "gear") {
-               gear(ps);
-       } else {
+       if(ps->nextc == '=') {
                expect(ps, '=');
-               expression(ps);
+               if(!expression(ps)) {
+                       return false;
+               }
+               expect(ps, ';');
+
+               set_var(ps, id, ps->val.top());
+               ps->val.pop();
+               ps->last_mod_var = id;
        }
 }
 
 static bool expression(ParserState *ps)
 {
+       // TODO
 }
 
-static bool ident(ParserState *ps, std::string *s)
+static char *get_ident(ParserState *ps, char *buf, int bsz)
 {
-       CHECK(expect_alpha(ps));
-       while(isalnum(ps->nextc) || ps->nextc == '_') {
-               s->append((char)ps->nextc);
-               CHECK(nextchar(ps));
+       char *ptr = buf;
+       if(!isalpha(ps->nextc)) {
+               expected(ps, "identifier");
+               return 0;
        }
-       return true;
+       while((isalnum(ps->nextc) || ps->nextc == '_')) {
+               if(bsz > 1) {
+                       *ptr++ = ps->nextc;
+                       --bsz;
+               }
+               nextchar(ps);
+       }
+       *ptr = 0;
+       return buf;
 }
 
 static bool skip_line(FILE *fp)
@@ -174,11 +182,17 @@ static bool nextchar(ParserState *ps)
 
        while((ps->nextc = fgetc(ps->fp)) != -1) {
                if(ps->nextc == '#') {
-                       CHECK(skip_line(ps->fp));
+                       if(!skip_line(ps->fp)) {
+                               return false;
+                       }
+                       ++ps->nline;
                }
                if(!isspace(ps->nextc)) {
                        break;
                }
+               if(ps->nextc == '\n') {
+                       ++ps->nline;
+               }
        }
 
        return ps->nextc != -1;
@@ -189,3 +203,118 @@ static void putback(ParserState *ps, char c)
        ps->savedc = ps->nextc;
        ps->nextc = c;
 }
+
+static void expected(ParserState *ps, const char *str)
+{
+       fprintf(stderr, "%s line %d error, expected: %s\n", ps->fname, ps->nline, str);
+       ++ps->nerrors;
+}
+
+static void set_var(ParserState *ps, const char *name, const Value &value)
+{
+       ps->var[name] = value;
+}
+
+static bool have_var(ParserState *ps, const char *name)
+{
+       return ps->var.find(name) != ps->var.end();
+}
+
+static const Value &get_var(ParserState *ps, const char *name)
+{
+       return ps->var[name];
+}
+
+static void print_value(const Value &val, FILE *fp)
+{
+       switch(val.type) {
+       case VAL_NUM:
+               fprintf(fp, "%f", val.v[0]);
+               break;
+
+       case VAL_VEC:
+               fprintf(fp, "[%f %f %f %f]", val.v[0], val.v[1], val.v[2], val.v[3]);
+               break;
+
+       case VAL_STR:
+               fprintf(fp, "\"%s\"", val.str);
+               break;
+
+       default:
+               fprintf(fp, "<invalid>");
+       }
+}
+
+// Gear-specific stuff
+
+static void update_gear_vars(ParserState *ps, Gear *gear)
+{
+       if(!ps->last_mod_var.empty()) {
+               const char *name = ps->last_mod_var.c_str();
+               set_gear_var(gear, name, get_var(ps, name));
+               ps->last_mod_var.clear();
+               return;
+       }
+
+       std::map<std::string, Value>::const_iterator it = ps->var.begin();
+       while(it != ps->var.end()) {
+               set_gear_var(gear, it->first.c_str(), it->second);
+               ++it;
+       }
+}
+
+
+#define ASSERT_TYPE(v, t) \
+       do { \
+               if((v).type != (t)) { \
+                       fprintf(stderr, "type mismatch while trying to set %s to value: ", name); \
+                       print_value(val, stderr); \
+                       fputc('\n', stderr); \
+                       return false; \
+               } \
+       } while(0)
+
+#define VVEC3(val) Vec3((val).v[0], (val).v[1], (val).v[2])
+#define VVEC4(val) Vec4((val).v[0], (val).v[1], (val).v[2], (val).v[3])
+
+static bool set_gear_var(Gear *gear, const char *name, const Value &val)
+{
+       if(strcmp(name, "position") == 0) {
+               ASSERT_TYPE(val, VAL_VEC);
+               gear->pos = VVEC3(val);
+
+       } else if(strcmp(name, "plane") == 0) {
+               ASSERT_TYPE(val, VAL_VEC);
+               gear->axis = VVEC3(val);
+               gear->pdist = val.v[3];
+
+       } else if(strcmp(name, "thickness") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               gear->thickness = val.v[0];
+
+       } else if(strcmp(name, "teeth") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               gear->set_teeth(val.v[0]);      // set teeth and recalc radius
+
+       } else if(strcmp(name, "teeth_length") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               gear->teeth_length = val.v[0];
+
+       } else if(strcmp(name, "pitch") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               gear->set_teeth(gear->nteeth, val.v[0]); // set pitch and recalc radius
+
+       } else if(strcmp(name, "color") == 0) {
+               ASSERT_TYPE(val, VAL_VEC);
+               gear->color = VVEC3(val);
+
+       } else if(strcmp(name, "roughness") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               gear->roughness = val.v[0];
+
+       } else {
+               return false;
+       }
+
+       return true;
+}