aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/sdlang/token.d
diff options
context:
space:
mode:
Diffstat (limited to 'src/sdlang/token.d')
-rw-r--r--src/sdlang/token.d135
1 files changed, 90 insertions, 45 deletions
diff --git a/src/sdlang/token.d b/src/sdlang/token.d
index 908d4a3..0a5b2fd 100644
--- a/src/sdlang/token.d
+++ b/src/sdlang/token.d
@@ -7,15 +7,18 @@ import std.array;
import std.base64;
import std.conv;
import std.datetime;
+import std.meta;
import std.range;
import std.string;
+import std.traits;
import std.typetuple;
import std.variant;
+import sdlang.exception;
import sdlang.symbol;
import sdlang.util;
-/// DateTime doesn't support milliseconds, but SDL's "Date Time" type does.
+/// DateTime doesn't support milliseconds, but SDLang's "Date Time" type does.
/// So this is needed for any SDL "Date Time" that doesn't include a time zone.
struct DateTimeFrac
{
@@ -30,11 +33,11 @@ struct DateTimeFrac
/++
If a "Date Time" literal in the SDL file has a time zone that's not found in
your system, you get one of these instead of a SysTime. (Because it's
-impossible to indicate "unknown time zone" with 'std.datetime.TimeZone'.)
+impossible to indicate "unknown time zone" with `std.datetime.TimeZone`.)
-The difference between this and 'DateTimeFrac' is that 'DateTimeFrac'
+The difference between this and `DateTimeFrac` is that `DateTimeFrac`
indicates that no time zone was specified in the SDL at all, whereas
-'DateTimeFracUnknownZone' indicates that a time zone was specified but
+`DateTimeFracUnknownZone` indicates that a time zone was specified but
data for it could not be found on your system.
+/
struct DateTimeFracUnknownZone
@@ -61,9 +64,10 @@ struct DateTimeFracUnknownZone
}
/++
-SDL's datatypes map to D's datatypes as described below.
+SDLang's datatypes map to D's datatypes as described below.
Most are straightforward, but take special note of the date/time-related types.
+---------------------------------------------------------------
Boolean: bool
Null: typeof(null)
Unicode Character: dchar
@@ -81,8 +85,9 @@ Date (with no time at all): Date
Date Time (no timezone): DateTimeFrac
Date Time (with a known timezone): SysTime
Date Time (with an unknown timezone): DateTimeFracUnknownZone
+---------------------------------------------------------------
+/
-alias TypeTuple!(
+alias ValueTypes = TypeTuple!(
bool,
string, dchar,
int, long,
@@ -90,41 +95,24 @@ alias TypeTuple!(
Date, DateTimeFrac, SysTime, DateTimeFracUnknownZone, Duration,
ubyte[],
typeof(null),
-) ValueTypes;
+);
-alias Algebraic!( ValueTypes ) Value; ///ditto
+alias Value = Algebraic!( ValueTypes ); ///ditto
+enum isValueType(T) = staticIndexOf!(T, ValueTypes) != -1;
-template isSDLSink(T)
-{
- enum isSink =
- isOutputRange!T &&
- is(ElementType!(T)[] == string);
-}
+enum isSink(T) =
+ isOutputRange!T &&
+ is(ElementType!(T)[] == string);
-string toSDLString(T)(T value) if(
- is( T : Value ) ||
- is( T : bool ) ||
- is( T : string ) ||
- is( T : dchar ) ||
- is( T : int ) ||
- is( T : long ) ||
- is( T : float ) ||
- is( T : double ) ||
- is( T : real ) ||
- is( T : Date ) ||
- is( T : DateTimeFrac ) ||
- is( T : SysTime ) ||
- is( T : DateTimeFracUnknownZone ) ||
- is( T : Duration ) ||
- is( T : ubyte[] ) ||
- is( T : typeof(null) )
-)
+string toSDLString(T)(T value) if(is(T==Value) || isValueType!T)
{
Appender!string sink;
toSDLString(value, sink);
return sink.data;
}
+/// Throws SDLangException if value is infinity, -infinity or NaN, because
+/// those are not currently supported by the SDLang spec.
void toSDLString(Sink)(Value value, ref Sink sink) if(isOutputRange!(Sink,char))
{
foreach(T; ValueTypes)
@@ -139,6 +127,52 @@ void toSDLString(Sink)(Value value, ref Sink sink) if(isOutputRange!(Sink,char))
throw new Exception("Internal SDLang-D error: Unhandled type of Value. Contains: "~value.toString());
}
+@("toSDLString on infinity and NaN")
+unittest
+{
+ import std.exception;
+
+ auto floatInf = float.infinity;
+ auto floatNegInf = -float.infinity;
+ auto floatNaN = float.nan;
+
+ auto doubleInf = double.infinity;
+ auto doubleNegInf = -double.infinity;
+ auto doubleNaN = double.nan;
+
+ auto realInf = real.infinity;
+ auto realNegInf = -real.infinity;
+ auto realNaN = real.nan;
+
+ assertNotThrown( toSDLString(0.0F) );
+ assertNotThrown( toSDLString(0.0) );
+ assertNotThrown( toSDLString(0.0L) );
+
+ assertThrown!ValidationException( toSDLString(floatInf) );
+ assertThrown!ValidationException( toSDLString(floatNegInf) );
+ assertThrown!ValidationException( toSDLString(floatNaN) );
+
+ assertThrown!ValidationException( toSDLString(doubleInf) );
+ assertThrown!ValidationException( toSDLString(doubleNegInf) );
+ assertThrown!ValidationException( toSDLString(doubleNaN) );
+
+ assertThrown!ValidationException( toSDLString(realInf) );
+ assertThrown!ValidationException( toSDLString(realNegInf) );
+ assertThrown!ValidationException( toSDLString(realNaN) );
+
+ assertThrown!ValidationException( toSDLString(Value(floatInf)) );
+ assertThrown!ValidationException( toSDLString(Value(floatNegInf)) );
+ assertThrown!ValidationException( toSDLString(Value(floatNaN)) );
+
+ assertThrown!ValidationException( toSDLString(Value(doubleInf)) );
+ assertThrown!ValidationException( toSDLString(Value(doubleNegInf)) );
+ assertThrown!ValidationException( toSDLString(Value(doubleNaN)) );
+
+ assertThrown!ValidationException( toSDLString(Value(realInf)) );
+ assertThrown!ValidationException( toSDLString(Value(realNegInf)) );
+ assertThrown!ValidationException( toSDLString(Value(realNaN)) );
+}
+
void toSDLString(Sink)(typeof(null) value, ref Sink sink) if(isOutputRange!(Sink,char))
{
sink.put("null");
@@ -194,18 +228,37 @@ void toSDLString(Sink)(long value, ref Sink sink) if(isOutputRange!(Sink,char))
sink.put( "%sL".format(value) );
}
+private void checkUnsupportedFloatingPoint(T)(T value) if(isFloatingPoint!T)
+{
+ import std.exception;
+ import std.math;
+
+ enforce!ValidationException(
+ !isInfinity(value),
+ "SDLang does not currently support infinity for floating-point types"
+ );
+
+ enforce!ValidationException(
+ !isNaN(value),
+ "SDLang does not currently support NaN for floating-point types"
+ );
+}
+
void toSDLString(Sink)(float value, ref Sink sink) if(isOutputRange!(Sink,char))
{
+ checkUnsupportedFloatingPoint(value);
sink.put( "%.10sF".format(value) );
}
void toSDLString(Sink)(double value, ref Sink sink) if(isOutputRange!(Sink,char))
{
+ checkUnsupportedFloatingPoint(value);
sink.put( "%.30sD".format(value) );
}
void toSDLString(Sink)(real value, ref Sink sink) if(isOutputRange!(Sink,char))
{
+ checkUnsupportedFloatingPoint(value);
sink.put( "%.30sBD".format(value) );
}
@@ -334,7 +387,7 @@ struct Token
{
Symbol symbol = sdlang.symbol.symbol!"Error"; /// The "type" of this token
Location location;
- Value value; /// Only valid when 'symbol' is symbol!"Value", otherwise null
+ Value value; /// Only valid when `symbol` is `symbol!"Value"`, otherwise null
string data; /// Original text from source
@disable this();
@@ -349,8 +402,8 @@ struct Token
/// Tokens with differing symbols are always unequal.
/// Tokens with differing values are always unequal.
/// Tokens with differing Value types are always unequal.
- /// Member 'location' is always ignored for comparison.
- /// Member 'data' is ignored for comparison *EXCEPT* when the symbol is Ident.
+ /// Member `location` is always ignored for comparison.
+ /// Member `data` is ignored for comparison *EXCEPT* when the symbol is Ident.
bool opEquals(Token b)
{
return opEquals(b);
@@ -376,13 +429,9 @@ struct Token
}
}
-version(sdlangUnittest)
+@("sdlang token")
unittest
{
- import std.stdio;
- writeln("Unittesting sdlang token...");
- stdout.flush();
-
auto loc = Location("", 0, 0, 0);
auto loc2 = Location("a", 1, 1, 1);
@@ -410,13 +459,9 @@ unittest
assert(Token(symbol!"Value",loc,Value(cast(float)1.2)) != Token(symbol!"Value",loc, Value(cast(double)1.2)));
}
-version(sdlangUnittest)
+@("sdlang Value.toSDLString()")
unittest
{
- import std.stdio;
- writeln("Unittesting sdlang Value.toSDLString()...");
- stdout.flush();
-
// Bool and null
assert(Value(null ).toSDLString() == "null");
assert(Value(true ).toSDLString() == "true");