-/* $Id$
-
-This file is part of libmspdatafile
-Copyright © 2007 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
+#include <limits>
#include <sys/param.h>
-#include <msp/strings/formatter.h>
+#include <msp/core/maputils.h>
+#include <msp/strings/format.h>
+#include "argumentstore.h"
#include "binaryparser.h"
+#include "binfloat.h"
#include "input.h"
+#include "loaderaction.h"
using namespace std;
namespace Msp {
namespace DataFile {
+class bad_definition: public runtime_error
+{
+public:
+ bad_definition(const std::string &w):
+ runtime_error(w)
+ { }
+
+ virtual ~bad_definition() throw() { }
+};
+
+class nesting_error: public logic_error
+{
+public:
+ nesting_error(const std::string &w):
+ logic_error(w)
+ { }
+
+ virtual ~nesting_error() throw() { }
+};
+
+
BinaryParser::BinaryParser(Input &i, const string &s):
ParserMode(i, s),
- first(true)
+ float_precision(32),
+ cur_info(0)
{
- dict[1]=DictEntry("__kwd", "iss");
- dict[2]=DictEntry("__str", "is");
+ dict[-1] = StatementInfo("__kwd", "iss");
+ dict[-2] = StatementInfo("__str", "is");
+ dict[-3] = StatementInfo("__flt", "i");
}
Statement BinaryParser::parse()
{
- while(1)
+ const StatementKey *key;
+ if(cur_info)
+ key = &cur_info->key;
+ else
{
- Statement st=parse_statement();
- if(st.keyword=="__kwd")
- {
- if(st.args.size()!=3)
- throw TypeError(src+": Keyword definition must have three arguments");
+ int id = parse_int();
+ if(!in)
+ return Statement();
- const unsigned id=st.args[0].get<unsigned>();
- const string &kw=st.args[1].get<const string &>();
- const string &args=st.args[2].get<const string &>();
- dict[id]=DictEntry(kw, args);
- }
- else if(st.keyword=="__str")
- {
- if(st.args.size()!=2)
- throw TypeError(src+": String definition must have two arguments");
-
- const unsigned id=st.args[0].get<unsigned>();
- strings[id]=st.args[1].get<const string &>();
- }
- else
- return st;
+ key = &get_item(dict, id).key;
}
-}
-
-Statement BinaryParser::parse_statement()
-{
- while(first && in.peek()=='\n')
- in.get();
- first=false;
-
- unsigned id=parse_int();
- if(!in)
- return Statement();
-
- Dictionary::const_iterator i=dict.find(id);
- if(i==dict.end())
- throw ParseError(format("%s: Unknown statement ID %d", src, id), src, 0);
- const DictEntry &de=i->second;
Statement result;
- result.keyword=de.keyword;
- result.source=src;
+ result.keyword = key->keyword;
+ result.source = src;
- for(unsigned j=0; j<de.args.size(); ++j)
+ for(unsigned j=0; j<key->signature.size(); ++j)
{
- switch(de.args[j])
+ switch(key->signature[j])
{
- case 'i':
+ case IntType::signature:
result.args.push_back(parse_int());
break;
- case 'f':
+ case FloatType::signature:
result.args.push_back(parse_float());
break;
- case 's':
+ case StringType::signature:
result.args.push_back(parse_string());
break;
- case 'b':
+ case BoolType::signature:
result.args.push_back(parse_bool());
break;
- case 'e':
- result.args.push_back(Value(ENUM, parse_enum()));
+ case SymbolType::signature:
+ result.args.push_back(parse_symbol());
break;
}
}
- unsigned nsub=parse_int();
- for(unsigned j=0; j<nsub; ++j)
+ if(!sub_remaining.empty())
+ --sub_remaining.back();
+
+ unsigned nsub = parse_int();
+ for(unsigned j = 0; j<nsub; ++j)
result.sub.push_back(parse());
- result.valid=true;
+ result.valid = true;
+ cur_info = 0;
return result;
}
-long long BinaryParser::parse_int()
+void BinaryParser::process_control_statement(const Statement &st)
+{
+ if(st.keyword=="__kwd")
+ {
+ int id = st.args[0].get<int>();
+ if(id<=0)
+ throw bad_definition("__kwd");
+
+ const string &kw = st.args[1].get<const string &>();
+ const string &args = st.args[2].get<const string &>();
+ for(string::const_iterator i=args.begin(); i!=args.end(); ++i)
+ for(unsigned j=0; valid_signatures[j]!=*i; ++j)
+ if(!valid_signatures[j])
+ throw bad_definition("__kwd");
+
+ dict[id] = StatementInfo(kw, args);
+ }
+ else if(st.keyword=="__str")
+ {
+ int id = st.args[0].get<int>();
+ if(id<=0)
+ throw bad_definition("__str");
+
+ strings[id] = st.args[1].get<const string &>();
+ }
+ else if(st.keyword=="__flt")
+ float_precision = st.args[0].get<unsigned>();
+}
+
+const StatementKey *BinaryParser::peek(unsigned level)
{
- long long result=0;
- unsigned bits=0;
+ if(level>sub_remaining.size())
+ throw nesting_error("bad level");
+ while(level<sub_remaining.size())
+ {
+ // Discard any substatements that haven't been parsed yet
+ for(unsigned i=sub_remaining.back(); i-->0; )
+ parse();
+ sub_remaining.pop_back();
+ cur_info = 0;
+ }
+
+ if(!sub_remaining.empty() && sub_remaining.back()==0)
+ {
+ // No more substatements on this level
+ cur_info = 0;
+ return 0;
+ }
+
+ if(cur_info)
+ return &cur_info->key;
+
+ int id = parse_int();
+ if(!in)
+ return 0;
+
+ cur_info = &get_item(dict, id);
+ return &cur_info->key;
+}
+
+bool BinaryParser::parse_and_load(unsigned level, Loader &ldr, const LoaderAction &act)
+{
+ if(!cur_info && !peek(level))
+ return false;
+
+ ArgumentStore args(*cur_info);
+ for(unsigned i=0; i<cur_info->key.signature.size(); ++i)
+ switch(cur_info->key.signature[i])
+ {
+ case IntType::signature:
+ args.set(i, parse_int());
+ break;
+ case FloatType::signature:
+ args.set(i, parse_float());
+ break;
+ case BoolType::signature:
+ args.set(i, parse_bool());
+ break;
+ case StringType::signature:
+ args.set(i, parse_string());
+ break;
+ case SymbolType::signature:
+ args.set(i, parse_symbol());
+ break;
+ }
+
+ if(!sub_remaining.empty())
+ --sub_remaining.back();
+ sub_remaining.push_back(parse_int());
+ cur_info = 0;
+
+ act.execute(ldr, args);
+
+ return true;
+}
+
+IntType::Store BinaryParser::parse_int()
+{
+ IntType::Store result = 0;
+ unsigned bits = 0;
while(in)
{
- int c=in.get();
+ int c = in.get();
- result=(result<<7) | (c&0x7F);
- bits+=7;
+ result = (result<<7) | (c&0x7F);
+ bits += 7;
if(!(c&0x80))
break;
}
- const long long mask=1LL<<(bits-1);
- result=(result^mask)-mask;
+ const IntType::Store mask = 1LL<<(bits-1);
+ result = (result^mask)-mask;
return result;
}
-float BinaryParser::parse_float()
+FloatType::Store BinaryParser::parse_float()
{
- union
+ UInt64 encoded = 0;
+ for(unsigned i=0; i<float_precision; i+=8)
+ {
+ int c = in.get();
+ encoded = (encoded<<8) | (c&0xFF);
+ }
+
+ BinFloat bf = BinFloat::explode(encoded, float_precision);
+
+ if(numeric_limits<FloatType::Store>::is_iec559)
+ return bf.compose_iec559<FloatType::Store>();
+ else
{
- float f;
- char d[sizeof(float)];
- };
-
-#if BYTE_ORDER == LITTLE_ENDIAN
- for(unsigned i=sizeof(float); i--;)
- d[i]=in.get();
-#else
- for(unsigned i=0; i<sizeof(float); ++i)
- d[i]=in.get();
-#endif
-
- return f;
+ /* Put the float together with arithmetic since we don't know its
+ internal layout */
+ FloatType::Store f = 0;
+ if(bf.infinity)
+ {
+ if(numeric_limits<FloatType::Store>::has_infinity)
+ f = numeric_limits<FloatType::Store>::infinity();
+ else
+ f = numeric_limits<FloatType::Store>::max();
+ }
+ else
+ {
+ for(unsigned i=0; i<64; ++i)
+ {
+ f /= 2;
+ if(bf.mantissa&1)
+ f += 1;
+ bf.mantissa >>= 1;
+ }
+ for(int i=0; i<bf.exponent; ++i)
+ f *= 2;
+ for(int i=0; i>bf.exponent; --i)
+ f /= 2;
+ }
+ if(bf.sign)
+ f = -f;
+ return f;
+ }
}
-bool BinaryParser::parse_bool()
+BoolType::Store BinaryParser::parse_bool()
{
return in.get();
}
-string BinaryParser::parse_string()
+StringType::Store BinaryParser::parse_string()
{
- int len=parse_int();
+ int len = parse_int();
if(len>=0)
{
string result;
result.reserve(len);
- for(int i=0; i<len; ++i)
- result+=in.get();
+ for(int i = 0; i<len; ++i)
+ result += in.get();
return result;
}
else
- return lookup_string(-len);
-}
-
-string BinaryParser::parse_enum()
-{
- return lookup_string(parse_int());
+ return get_item(strings, -len);
}
-const string &BinaryParser::lookup_string(unsigned id) const
+SymbolType::Store BinaryParser::parse_symbol()
{
- StringMap::const_iterator i=strings.find(id);
- if(i==strings.end())
- throw KeyError("Unknown string", lexical_cast(id));
- return i->second;
+ return get_item(strings, parse_int());
}
} // namespace DataFile