/* 6D programming language Copyright (C) 2011 Danny Milosavljevic This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "Scanners/Scanner" #include #include #include "6D/Values" #include "6D/FFIs" #include "Values/Values" BEGIN_NAMESPACE_6D(Scanners) #undef GETC #undef UNGETC #define GETC fgetc(self->file) #define UNGETC(c) ungetc(c, self->file) BEGIN_STRUCT_6D(Scanner) NODET value; FILE* file; const char* name; int linenumber; bool bBeginningOfLine; NodeT env; NODET indents; NativeUInt indent; /* of current line */ NODET SEOF; bool bEOF; END_STRUCT_6D(Scanner) static INLINE int integerCompareD(NODET a, NATIVEINT bvalue) { NATIVEINT avalue; if(!toNativeInt(a, &avalue)) abort(); return (avalue < bvalue) ? (-1) : (avalue == bvalue) ? 0 : 1; } /** \return whether we already injected a token or not. */ static bool Scanner_skipWhitespace(struct Scanner* self) { int c; if(!self->bEOF) while((c = GETC) != EOF && c != 26/*EOF*/) { if(c == ' ' || c == '\t') { if(self->bBeginningOfLine) { self->indent += 1; if(self->indent == 0) { /* overflow */ abort(); } } } else if(c == '\r') { } else if(c == '\n') { self->bBeginningOfLine = true; self->indent = 0; //self->value = env.SLF; ++self->linenumber; //return true; } else if(self->bBeginningOfLine) { /* actual text for the first time on the line */ UNGETC(c); /* make sure the user will see the actual text */ self->bBeginningOfLine = false; int status = integerCompareD(consHead(self->indents), self->indent); if(status < 0) { self->indents = cons(internNativeInt(self->indent), self->indents); self->value = env_Sindent(self->env); return true; } else if(status > 0) { int diff; self->value = env_Sdedent(self->env); self->indents = consTail(self->indents); /* decrease indentation by ONE level */ assert(!nilP(self->indents)); /* 0 will never be removed */ diff = integerCompareD(consHead(self->indents), self->indent); if(diff < 0) { /* self indentation does not exist - it would have been in-between */ self->value = env_error(self->env, "", ""); return true; } else if(diff > 0) { /* there are others to be closed (will be done at the next iteration). */ self->bBeginningOfLine = true; /* make sure to check at the next iteration, too */ return true; } else { /* self one is the right one */ return true; } } return false; } else { UNGETC(c); /* make sure the user will see the actual text */ return false; } } self->bEOF = true; return false; } void Scanner_init(struct Scanner* self, NodeT aEnv) { initIntegers(); self->value = nil; self->name = NULL; self->linenumber = 1; self->bBeginningOfLine = true; self->env = aEnv; self->indent = 0; self->indents = cons(internNativeInt((NativeInt) 0), nil); self->SEOF = env_SEOF(self->env); self->bEOF = false; } void Scanner_push(struct Scanner* self, FILE* file, int linenumber, const char* name) { /* TODO support "include" files? */ self->file = file; self->linenumber = linenumber; self->name = name; } void Scanner_pop(struct Scanner* self) { } size_t Scanner_getSize(void) { return sizeof(struct Scanner); } NODET Scanner_consume(struct Scanner* self) { NODET previousValue = self->value; if(!Scanner_skipWhitespace(self)) { if(self->bEOF) { /* TODO unlikely */ if(self->indents && consTail(self->indents)) { self->indents = consTail(self->indents); self->value = env_Sdedent(self->env); } else self->value = env_SEOF(self->env); } else { /* one of the less nice things is that self will not report back that it found EOF so we will try to read again. */ self->value = env_readToken(self->env, self->file, &self->linenumber); } } return previousValue; } /*NODET consume1(NODET expectedToken) { if(self->value != expectedToken) return env.error(symbolName(expectedToken), symbolName(self->value)); else return consume(expectedToken); }*/ NODET Scanner_getToken(const struct Scanner* self) { return self->value; } int Scanner_getLinenumber(const struct Scanner* self) { return self->linenumber; } END_NAMESPACE_6D(Scanners)