[Nickle] nickle: Branch 'master'

Keith Packard keithp at keithp.com
Tue Nov 14 00:24:09 PST 2017


 Makefile.am          |    2 
 builtin-date.c       |  224 +++++++++++++++++++++++++++++++++++++++++++++++++++
 builtin-namespaces.h |    1 
 builtin.c            |    1 
 integer.c            |   34 ++++++-
 natural.c            |   17 +++
 nickle.h             |    1 
 test/Makefile.am     |    3 
 test/datetest.5c     |   62 ++++++++++++++
 value.c              |   27 ++++++
 value.h              |   39 ++++++--
 11 files changed, 392 insertions(+), 19 deletions(-)

New commits:
commit 4945fbf7a6852245094e4425c1f416fa08ac9c6d
Author: Keith Packard <keithp at keithp.com>
Date:   Mon Nov 13 15:49:38 2017 -0800

    Add date conversion functions localtime, gmtime, timelocal, timegm
    
    These just wrap the C versions, except they report actual years,
    rather than years since 1900 and January is month 1, not 0.
    
    Signed-off-by: Keith Packard <keithp at keithp.com>

diff --git a/Makefile.am b/Makefile.am
index 4c09141..7f1b497 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -52,7 +52,7 @@ nickle_SOURCES = \
 	builtin-file.c builtin-math.c builtin-namespaces.h \
 	builtin-semaphore.c builtin-sockets.c builtin-string.c \
 	builtin-thread.c builtin-toplevel.c builtin-pid.c \
-	builtin.c builtin.h \
+	builtin-date.c builtin.c builtin.h \
 	builtin-foreign.c gram.y lex.l
 
 nickle_LDFLAGS=$(NICKLE_LDFLAGS)
diff --git a/builtin-date.c b/builtin-date.c
new file mode 100644
index 0000000..5d5e1f4
--- /dev/null
+++ b/builtin-date.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright © 2017 Keith Packard and Bart Massey.
+ * All Rights Reserved.  See the file COPYING in this directory
+ * for licensing information.
+ */
+
+/*
+ *	date.c
+ *
+ *	provide builtin functions for the Date namespace
+ */
+
+#include	<ctype.h>
+#include	<strings.h>
+#include	<time.h>
+#include	"builtin.h"
+
+NamespacePtr DateNamespace;
+
+static Type *typeDate;
+
+#define DATE_I	0
+#define DATE_S	"00"
+
+static Value
+int_value(int s)
+{
+    return Reduce(NewSignedDigitInteger((signed_digit) s));
+}
+
+static int
+value_int(Value s, Atom member, char *error, int def)
+{
+    Value	ref = StructMemRef(s, AtomId(member));
+    Value	mem;
+
+    if (ref == 0)
+	return def;
+
+    mem = RefValueGet(ref);
+    if (mem == 0)
+	return def;
+    return IntPart(mem, error);
+}
+
+static int
+value_bool(Value s, Atom member, char *error, int def)
+{
+    Value	ref = StructMemRef(s, AtomId(member));
+    Value	mem;
+
+    if (ref == 0)
+	return def;
+
+    mem = RefValueGet(ref);
+    if (mem == 0)
+	return def;
+
+    return BoolPart(mem, error);
+}
+
+static Value
+to_date(struct tm *tm)
+{
+    Value ret;
+    BoxPtr	box;
+
+    ret = NewStruct(TypeCanon(typeDate)->structs.structs, False);
+    box = ret->structs.values;
+    BoxValueSet (box, 0, int_value(tm->tm_sec));
+    BoxValueSet (box, 1, int_value(tm->tm_min));
+    BoxValueSet (box, 2, int_value(tm->tm_hour));
+    BoxValueSet (box, 3, int_value(tm->tm_mday));
+    BoxValueSet (box, 4, int_value(tm->tm_mon + 1));
+    BoxValueSet (box, 5, int_value(tm->tm_year + 1900));
+    BoxValueSet (box, 6, int_value(tm->tm_wday));
+    BoxValueSet (box, 7, int_value(tm->tm_yday));
+    BoxValueSet (box, 8, tm->tm_isdst ? TrueVal : FalseVal);
+    BoxValueSet (box, 9, NewStrString(tm->tm_zone));
+    return ret;
+}
+
+static void
+from_date(Value date, struct tm *tm)
+{
+    BoxPtr	box = date->structs.values;
+
+    tm->tm_sec = value_int(date, "sec", "invalid sec", 0);
+    tm->tm_min = value_int(date, "min", "invalid min", 0);
+    tm->tm_hour = value_int(date, "hour", "invalid hour", 0);
+    tm->tm_mday = value_int(date, "mday", "invalid mday", 1);
+    tm->tm_mon = value_int(date, "mon", "invalid mon", 1) - 1;
+    tm->tm_year = value_int(date, "year", "invalid year", 1970) - 1900;
+    tm->tm_wday = value_int(date, "wday", "invalid wday", 0);
+    tm->tm_yday = value_int(date, "yday", "invalid yday", 0);
+    tm->tm_isdst = value_bool(date, "isdst", "invalid isdst", 0);
+    tm->tm_zone = NULL;
+}
+
+static Value
+do_Date_gmtime(Value v)
+{
+    ENTER();
+    time_t	seconds = SignedDigitPart(v, "Illegal time");
+    struct tm	result;
+
+    if (aborting)
+	RETURN(Void);
+
+    gmtime_r(&seconds, &result);
+    RETURN(to_date(&result));
+}
+
+static Value
+do_Date_localtime(Value v)
+{
+    ENTER();
+    time_t	seconds = SignedDigitPart(v, "Illegal time");
+    struct tm	result;
+
+    if (aborting)
+	RETURN(Void);
+
+    localtime_r(&seconds, &result);
+    RETURN(to_date(&result));
+}
+
+static Value
+do_Date_timegm(Value v)
+{
+    ENTER();
+    struct tm	tm;
+    time_t	seconds;
+
+    from_date(v, &tm);
+    seconds = timegm(&tm);
+    RETURN(Reduce(NewSignedDigitInteger((signed_digit) seconds)));
+}
+
+static Value
+do_Date_timelocal(Value v)
+{
+    ENTER();
+    struct tm	tm;
+    time_t	seconds;
+
+    from_date(v, &tm);
+    seconds = timelocal(&tm);
+    RETURN(Reduce(NewSignedDigitInteger((signed_digit) seconds)));
+}
+
+static Type *
+make_typedef (char	*name_str,
+	      Namespace	*namespace,
+	      Publish	publish,
+	      int	usertype_id,
+	      Symbol	**sret,
+	      Type	*type)
+{
+    ENTER ();
+    Atom    name = AtomId (name_str);
+    Symbol  *sym = NewSymbolType (name, type);
+    Type    *typed = NewTypeName (NewExprAtom (name, 0, False),
+				  sym);
+
+    NamespaceAddName (namespace, sym, publish);
+
+    BuiltinSetUserdefType (typed, usertype_id);
+    MemAddRoot (typed);
+    if (sret)
+	*sret = sym;
+    RETURN (typed);
+}
+
+void
+import_Date_namespace()
+{
+    ENTER ();
+    static const struct fbuiltin_1 funcs_1[] = {
+        { do_Date_gmtime, "gmtime", DATE_S, "i", "\n"
+	    " date_t gmtime (int time)\n"
+	    "\n"
+	    " Convert 'time' into a date_t structure using UTC.\n" },
+	{ do_Date_localtime, "localtime", DATE_S, "i", "\n"
+	    " date_t localtime (int time)\n"
+	    "\n"
+	    " Convert 'time' into a date_t structure using the local timezone.\n" },
+	{ do_Date_timegm, "timegm", "i", DATE_S, "\n"
+	    " int timegm (date_t date)\n"
+	    "\n"
+	    " Convert 'date' into seconds using UTC.\n" },
+	{ do_Date_timelocal, "timelocal", "i", DATE_S, "\n"
+	    " int timelocal (date_t date)\n"
+	    "\n"
+	    " Convert 'date' into seconds using the local timezone.\n" },
+	{ do_Date_timelocal, "mktime", "i", DATE_S, "\n"
+	    " int mktime (date_t date)\n"
+	    "\n"
+	    " Convert 'date' into seconds using the local timezone.\n" },
+        { 0 }
+    };
+
+    DateNamespace = BuiltinNamespace (/*parent*/ 0, "Date")->namespace.namespace;
+
+    typeDate = make_typedef("date_t",
+			    DateNamespace,
+			    publish_public,
+			    DATE_I,
+			    NULL,
+			    BuildStructType (10,
+					     typePrim[rep_integer], "sec",
+					     typePrim[rep_integer], "min",
+					     typePrim[rep_integer], "hour",
+					     typePrim[rep_integer], "mday",
+					     typePrim[rep_integer], "mon",
+					     typePrim[rep_integer], "year",
+					     typePrim[rep_integer], "wday",
+					     typePrim[rep_integer], "yday",
+					     typePrim[rep_bool], "isdst",
+					     typePrim[rep_string], "zone"));
+
+    BuiltinFuncs1 (&DateNamespace, funcs_1);
+    EXIT ();
+}
diff --git a/builtin-namespaces.h b/builtin-namespaces.h
index df4fc89..d0c3728 100644
--- a/builtin-namespaces.h
+++ b/builtin-namespaces.h
@@ -24,3 +24,4 @@ extern void import_Environ_namespace(void);
 extern void import_Socket_namespace(void);
 extern void import_Foreign_namespace(void);
 extern void import_PID_namespace(void);
+extern void import_Date_namespace(void);
diff --git a/builtin.c b/builtin.c
index c0fb919..cd65b18 100644
--- a/builtin.c
+++ b/builtin.c
@@ -335,6 +335,7 @@ BuiltinInit (void)
     import_Socket_namespace();
     import_Foreign_namespace ();
     import_PID_namespace ();
+    import_Date_namespace();
 
     /* Import builtin strings with predefined values */
     BuiltinStrings (svars);
diff --git a/integer.c b/integer.c
index affaf0d..bc82c28 100644
--- a/integer.c
+++ b/integer.c
@@ -28,6 +28,26 @@ IntegerToInt (Integer *i)
     return result;
 }
 
+signed_digit
+IntegerToSignedDigit(Integer *i)
+{
+    double_digit	dd;
+    signed_digit	sd;
+
+    dd = NaturalToDoubleDigit(IMag(i));
+    if (ISign(i) == Negative)
+	sd = -(signed_digit) dd;
+    else
+	sd = (signed_digit) dd;
+    return sd;
+}
+
+int
+IntegerFitsSignedDigit(Integer *i)
+{
+    return NaturalLess(IMag(i), max_signed_digit_natural);
+}
+
 static Value
 IntegerPlus (Value av, Value bv, int expandOk)
 {
@@ -96,7 +116,7 @@ IntegerTimes (Value av, Value bv, int expandOk)
     ENTER ();
     Integer	*a = &av->integer, *b = &bv->integer;
     Sign	sign;
-    
+
     sign = Positive;
     if (ISign(a) != ISign(b))
 	sign = Negative;
@@ -110,7 +130,7 @@ IntegerDivide (Value av, Value bv, int expandOk)
     Integer	*a = &av->integer, *b = &bv->integer;
     Natural	*rem;
     Sign	sign;
-    
+
     if (NaturalZero (IMag(b)))
     {
 	RaiseStandardException (exception_divide_by_zero, 2,
@@ -133,7 +153,7 @@ IntegerDiv (Value av, Value bv, int expandOk)
     Integer	*a = &av->integer, *b = &bv->integer;
     Sign	sign;
     Natural	*quo, *rem;
-    
+
     if  (NaturalZero (IMag(b)))
     {
 	RaiseStandardException (exception_divide_by_zero, 2,
@@ -155,7 +175,7 @@ IntegerMod (Value av, Value bv, int expandOk)
     ENTER ();
     Integer	*a = &av->integer, *b = &bv->integer;
     Natural	*rem;
-    
+
     if  (NaturalZero (IMag(b)))
     {
 	RaiseStandardException (exception_divide_by_zero, 2,
@@ -308,7 +328,7 @@ static Value
 IntegerReduce (Value av)
 {
     Integer *a = &av->integer;
-    
+
     if (NaturalLess (IMag(a), max_int_natural))
 	av = NewInt (IntegerToInt (a));
     return av;
@@ -322,7 +342,7 @@ IntegerPrint (Value f, Value iv, char format, int base, int width, int prec, int
     char    *result;
     int	    print_width;
     int	    fraction_width;
-    
+
     if (base == 0)
 	base = 10;
     result = NaturalSprint (0, IMag(i), base, &print_width);
@@ -392,7 +412,7 @@ IntegerMark (void *object)
     MemReference (IMag(integer));
 }
 
-ValueRep    IntegerRep = { 
+ValueRep    IntegerRep = {
     { IntegerMark, 0, "IntegerRep" },	    /* base */
     rep_integer,	    /* tag */
     {			    /* binary */
diff --git a/natural.c b/natural.c
index 6459cfb..3aa54af 100644
--- a/natural.c
+++ b/natural.c
@@ -31,6 +31,7 @@ Natural	*zero_natural;
 Natural	*one_natural;
 Natural *two_natural;
 Natural *max_int_natural;
+Natural *max_signed_digit_natural;
 #ifndef LBASE10
 static Natural *max_tenpow_natural;
 static int	tenpow_digits;
@@ -51,6 +52,20 @@ NaturalToInt (Natural *n)
     return i;
 }
 
+double_digit
+NaturalToDoubleDigit(Natural *n)
+{
+    double_digit	i;
+    digit		*d;
+    int			index;
+
+    d = data(n) + length (n);
+    i = 0;
+    for (index = 0; index < length(n); index++)
+	i = i * BASE + (double_digit) *--d;
+    return i;
+}
+
 void
 NaturalCopy (Natural *a, Natural *b)
 {
@@ -1451,6 +1466,8 @@ NaturalInit (void)
     MemAddRoot (two_natural);
     max_int_natural = NewDoubleDigitNaturalReal (MAX_NICKLE_INT);
     MemAddRoot (max_int_natural);
+    max_signed_digit_natural = NewDoubleDigitNaturalReal (MAX_NICKLE_SIGNED_DIGIT);
+    MemAddRoot(max_signed_digit_natural);
 #ifndef LBASE10
     tenpow_digits = (int) floor (log10 ((double) MAX_NICKLE_INT));
     max_tenpow = 1;
diff --git a/nickle.h b/nickle.h
index 3c900e7..c57921e 100644
--- a/nickle.h
+++ b/nickle.h
@@ -695,6 +695,7 @@ extern NamespacePtr	CommandNamespace;
 extern NamespacePtr	GcdNamespace;
 #endif
 extern NamespacePtr	EnvironNamespace;
+extern NamespacePtr	DateNamespace;
 
 void	BuiltinInit (void);
 
diff --git a/test/Makefile.am b/test/Makefile.am
index 7794477..e1fc3f0 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -13,7 +13,8 @@ check_SCRIPTS=\
 	math.5c \
 	factorial.5c \
 	is_type.5c \
-	jsontest.5c
+	jsontest.5c \
+	datetest.5c
 
 noinst_PROGRAMS=math-tables
 
diff --git a/test/datetest.5c b/test/datetest.5c
new file mode 100644
index 0000000..b516c86
--- /dev/null
+++ b/test/datetest.5c
@@ -0,0 +1,62 @@
+import Date;
+
+exception bad_seconds(int want, date_t date, int got);
+
+exception bad_date(date_t want, int seconds, date_t got);
+
+void test_round_seconds(int seconds) {
+    date_t	g = gmtime(seconds);
+    date_t	l = localtime(seconds);
+    int		sg = timegm(g);
+    int		sl = timelocal(l);
+
+    if (seconds != sg)
+	raise bad_seconds(seconds, g, sg);
+    if (seconds != sl)
+	raise bad_seconds(seconds, l, sl);
+}
+
+void test_round_date(date_t date) {
+    int		sg = timegm(date);
+    int		sl = timelocal(date);
+    date_t	g = gmtime(sg);
+    date_t	l = localtime(sl);
+
+    if (g != date)
+	raise bad_date(date, sg, g);
+    if (l != date)
+	raise bad_date(date, sl, l);
+}
+
+void test_utc_seconds(int seconds, date_t date) {
+    date_t	got_date = gmtime(seconds);
+    int		got_seconds = timegm(date);
+
+    if (got_date != date)
+	raise bad_date(date, seconds, got_date);
+    if (got_seconds != seconds)
+	raise bad_seconds(seconds, date, got_seconds);
+}
+
+test_round_seconds(0);
+test_round_seconds(time());
+
+/* Epoch */
+test_utc_seconds(0,
+		 (date_t) {
+		     .sec = 0,
+		     .min = 0,
+		     .hour = 0,
+		     .mday = 1,
+		     .mon = 1,
+		     .wday = 4,
+		     .yday = 0,
+		     .year = 1970,
+		     .isdst = false,
+		     .zone = "GMT"
+			 });
+
+/* Make sure 2038 problem isn't present */
+test_utc_seconds(2**31,
+		 (date_t) {sec = 8, min = 14, hour = 3, mday = 19, mon = 1, year = 2038,
+			 wday = 2, yday = 18, isdst = false, zone = "GMT"});
diff --git a/value.c b/value.c
index 7add3cd..b58b9bd 100644
--- a/value.c
+++ b/value.c
@@ -108,6 +108,33 @@ IntPart (Value av, char *error)
     return ValueInt(av);
 }
 
+int
+BoolPart (Value av, char *error)
+{
+    if (!ValueIsBool(av))
+    {
+	RaiseStandardException (exception_invalid_argument, 3,
+				NewStrString (error), 
+				NewInt (0), av);
+	return 0;
+    }
+    return av == TrueVal;
+}
+
+signed_digit
+SignedDigitPart(Value av, char *error)
+{
+    if (ValueIsInt(av))
+	return ValueInt(av);
+    if (ValueIsInteger(av) && IntegerFitsSignedDigit(&av->integer))
+	return IntegerToSignedDigit(&av->integer);
+
+    RaiseStandardException (exception_invalid_argument, 3,
+			    NewStrString (error), 
+			    NewInt (0), av);
+    return 0;
+}
+
 Value
 BinaryOperate (Value av, Value bv, BinaryOp operator)
 {
diff --git a/value.h b/value.h
index 365b18f..389aff7 100644
--- a/value.h
+++ b/value.h
@@ -169,7 +169,7 @@ typedef unsigned short digit;
 			 ((double_digit) NaturalDigits(n)[i] << LBASE2))
 #define ModBase(t)  ((t) & (((double_digit) 1 << LBASE2) - 1))
 #define DivBase(t)  ((t) >> LBASE2)
-    
+
 /* HashValues are stored in rep_int */
 
 typedef int HashValue;
@@ -236,6 +236,7 @@ int	IntWidth (int i);
 int	DoubleDigitWidth (double_digit i);
 HashValue   NaturalHash (Natural *a);
 
+extern Natural	*max_signed_digit_natural;
 extern Natural	*max_int_natural;
 extern Natural	*zero_natural;
 extern Natural	*one_natural;
@@ -288,11 +289,11 @@ typedef enum _rep {
 	rep_bool = 9,
 	rep_foreign = 10,
 	rep_void = 11,
-	
+
 	/* composite types */
 	rep_ref = 12,
 	rep_func = 13,
-    
+
 	/* mutable type */
  	rep_array = 14,
 	rep_struct = 15,
@@ -347,6 +348,7 @@ static inline Sign IntSign(int i) {
 
 #define MAX_NICKLE_INT	    ((int) ((unsigned) NICKLE_INT_SIGN - 1))
 #define MIN_NICKLE_INT	    (-MAX_NICKLE_INT - 1)
+#define MAX_NICKLE_SIGNED_DIGIT	((signed_digit) (((double_digit) 1 << (sizeof(signed_digit) * 8 - 1)) - 1))
 
 #define One NewInt(1)
 #define Zero NewInt(0)
@@ -354,7 +356,7 @@ static inline Sign IntSign(int i) {
 static inline Bool ValueIsPtr (Value v) {
 	return (PtrToInt(v) & 1) == 0;
 }
-	
+
 static inline Bool ValueIsInt (Value v) {
 	return !ValueIsPtr(v);
 }
@@ -400,10 +402,10 @@ ArgType *NewArgType (TypePtr type, Bool varargs, Atom name,
 		     SymbolPtr symbol, ArgType *next);
 
 typedef enum _typeTag {
-    type_prim, type_name, type_ref, type_func, type_array, 
+    type_prim, type_name, type_ref, type_func, type_array,
     type_struct, type_union, type_types, type_hash
 } TypeTag;
-    
+
 typedef struct _typeBase {
     DataType	*data;
     TypeTag	tag;
@@ -464,7 +466,7 @@ typedef struct _typeStruct {
     StructTypePtr   structs;
     Bool	    enumeration;
     TypePtr	    left, right;
-} TypeStruct;    
+} TypeStruct;
 
 typedef struct _typeElt {
     DataType	    *data;
@@ -1336,12 +1338,29 @@ extern Bool signalError;	/* current thread run time error */
 #define SetSignalException()(aborting = signaling = signalException = True)
 #define SetSignalError()    (aborting = signaling = signalError = True)
 
-int	NaturalToInt (Natural *);
-int	IntegerToInt (Integer *);
+int
+NaturalToInt (Natural *n);
+
+double_digit
+NaturalToDoubleDigit(Natural *n);
+
+int	IntegerToInt (Integer *i);
+
+int
+IntegerFitsSignedDigit(Integer *i);
+
+signed_digit
+IntegerToSignedDigit(Integer *i);
+
 int	IntPart (Value, char *error);
 
+int	BoolPart (Value, char *error);
+
+signed_digit
+SignedDigitPart(Value v, char *error);
+
 double	DoublePart (Value av, char *error);
-    
+
 Bool	Zerop (Value);
 Bool	Negativep (Value);
 Bool	Evenp (Value);


More information about the Nickle mailing list