1 // SDLang-D
2 // Written in the D programming language.
3 
4 module sdlang.util;
5 
6 import std.algorithm;
7 import std.array;
8 import std.conv;
9 import std.datetime;
10 import std.range;
11 import std.stdio;
12 import std..string;
13 
14 import sdlang.exception;
15 import sdlang.token;
16 
17 enum sdlangVersion = "0.9.1";
18 
19 alias immutable(ubyte)[] ByteString;
20 
21 auto startsWith(T)(string haystack, T needle)
22 	if( is(T:ByteString) || is(T:string) )
23 {
24 	return std.algorithm.startsWith( cast(ByteString)haystack, cast(ByteString)needle );
25 }
26 
27 struct Location
28 {
29 	string file; /// Filename (including path)
30 	int line; /// Zero-indexed
31 	int col;  /// Zero-indexed, Tab counts as 1
32 	size_t index; /// Index into the source
33 
34 	this(int line, int col, int index)
35 	{
36 		this.line  = line;
37 		this.col   = col;
38 		this.index = index;
39 	}
40 
41 	this(string file, int line, int col, int index)
42 	{
43 		this.file  = file;
44 		this.line  = line;
45 		this.col   = col;
46 		this.index = index;
47 	}
48 
49 	/// Convert to string. Optionally takes output range as a sink.
50 	string toString()
51 	{
52 		Appender!string sink;
53 		this.toString(sink);
54 		return sink.data;
55 	}
56 
57 	///ditto
58 	void toString(Sink)(ref Sink sink) if(isOutputRange!(Sink,char))
59 	{
60 		sink.put(file);
61 		sink.put("(");
62 		sink.put(to!string(line+1));
63 		sink.put(":");
64 		sink.put(to!string(col+1));
65 		sink.put(")");
66 	}
67 }
68 
69 struct FullName
70 {
71 	string namespace;
72 	string name;
73 
74 	/// Convert to string. Optionally takes output range as a sink.
75 	string toString()
76 	{
77 		if(namespace == "")
78 			return name;
79 
80 		Appender!string sink;
81 		this.toString(sink);
82 		return sink.data;
83 	}
84 
85 	///ditto
86 	void toString(Sink)(ref Sink sink) if(isOutputRange!(Sink,char))
87 	{
88 		if(namespace != "")
89 		{
90 			sink.put(namespace);
91 			sink.put(":");
92 		}
93 
94 		sink.put(name);
95 	}
96 
97 	///
98 	static string combine(string namespace, string name)
99 	{
100 		return FullName(namespace, name).toString();
101 	}
102 	///
103 	@("FullName.combine example")
104 	unittest
105 	{
106 		assert(FullName.combine("", "name") == "name");
107 		assert(FullName.combine("*", "name") == "*:name");
108 		assert(FullName.combine("namespace", "name") == "namespace:name");
109 	}
110 
111 	///
112 	static FullName parse(string fullName)
113 	{
114 		FullName result;
115 		
116 		auto parts = fullName.findSplit(":");
117 		if(parts[1] == "") // No colon
118 		{
119 			result.namespace = "";
120 			result.name      = parts[0];
121 		}
122 		else
123 		{
124 			result.namespace = parts[0];
125 			result.name      = parts[2];
126 		}
127 
128 		return result;
129 	}
130 	///
131 	@("FullName.parse example")
132 	unittest
133 	{
134 		assert(FullName.parse("name") == FullName("", "name"));
135 		assert(FullName.parse("*:name") == FullName("*", "name"));
136 		assert(FullName.parse("namespace:name") == FullName("namespace", "name"));
137 	}
138 
139 	/// Throws with appropriate message if this.name is "*".
140 	/// Wildcards are only supported for namespaces, not names.
141 	void ensureNoWildcardName(string extaMsg = null)
142 	{
143 		if(name == "*")
144 			throw new ArgumentException(`Wildcards ("*") only allowed for namespaces, not names. `~extaMsg);
145 	}
146 }
147 struct Foo { string foo; }
148 
149 void removeIndex(E)(ref E[] arr, ptrdiff_t index)
150 {
151 	arr = arr[0..index] ~ arr[index+1..$];
152 }
153 
154 void trace(string file=__FILE__, size_t line=__LINE__, TArgs...)(TArgs args)
155 {
156 	version(sdlangTrace)
157 	{
158 		writeln(file, "(", line, "): ", args);
159 		stdout.flush();
160 	}
161 }
162 
163 string toString(TypeInfo ti)
164 {
165 	if     (ti == typeid( bool         )) return "bool";
166 	else if(ti == typeid( string       )) return "string";
167 	else if(ti == typeid( dchar        )) return "dchar";
168 	else if(ti == typeid( int          )) return "int";
169 	else if(ti == typeid( long         )) return "long";
170 	else if(ti == typeid( float        )) return "float";
171 	else if(ti == typeid( double       )) return "double";
172 	else if(ti == typeid( real         )) return "real";
173 	else if(ti == typeid( Date         )) return "Date";
174 	else if(ti == typeid( DateTimeFrac )) return "DateTimeFrac";
175 	else if(ti == typeid( DateTimeFracUnknownZone )) return "DateTimeFracUnknownZone";
176 	else if(ti == typeid( SysTime      )) return "SysTime";
177 	else if(ti == typeid( Duration     )) return "Duration";
178 	else if(ti == typeid( ubyte[]      )) return "ubyte[]";
179 	else if(ti == typeid( typeof(null) )) return "null";
180 
181 	return "{unknown}";
182 }
183 
184 enum BOM {
185 	UTF8,           /// UTF-8
186 	UTF16LE,        /// UTF-16 (little-endian)
187 	UTF16BE,        /// UTF-16 (big-endian)
188 	UTF32LE,        /// UTF-32 (little-endian)
189 	UTF32BE,        /// UTF-32 (big-endian)
190 }
191 
192 enum NBOM = __traits(allMembers, BOM).length;
193 immutable ubyte[][NBOM] ByteOrderMarks =
194 [
195 	[0xEF, 0xBB, 0xBF],         //UTF8
196 	[0xFF, 0xFE],               //UTF16LE
197 	[0xFE, 0xFF],               //UTF16BE
198 	[0xFF, 0xFE, 0x00, 0x00],   //UTF32LE
199 	[0x00, 0x00, 0xFE, 0xFF]    //UTF32BE
200 ];