[yaml] add sources

This commit is contained in:
2020-02-13 17:20:50 +03:00
parent 39ca4b3d9b
commit c889ca04da
33 changed files with 4338 additions and 10 deletions

View File

@@ -8,14 +8,13 @@
"template"
],
"description": "View framework.",
"version": "1.1.0",
"version": "1.2.0",
"releasenote": "Update.",
"contributors": [
"shmyga"
],
"classPath": "src/main",
"dependencies": {
"promhx": "1.1.0",
"yaml": "1.3.0"
"promhx": "1.1.0"
}
}

View File

@@ -12,11 +12,11 @@ class LoggerUtil {
public static function printStackItem(item:StackItem):String {
return switch item {
case StackItem.CFunction: 'CFunction';
case StackItem.Module(m): m;
case StackItem.FilePos(s, file, line): '${file}:${line}';
case StackItem.Method(classname, method): '${classname}::${method}}';
case StackItem.LocalFunction(v): 'LocalFunction(${v})';
case CFunction: 'CFunction';
case Module(m): m;
case FilePos(s, file, line): '${file}:${line}';
case Method(classname, method): '${classname}::${method}}';
case LocalFunction(v): 'LocalFunction(${v})';
}
}
@@ -55,4 +55,4 @@ class BaseLogger implements ILogger {
public function e(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void {
log(LogLevel.ERROR, tag, message, error, p);
}
}
}

View File

@@ -27,7 +27,9 @@ class TraceLogger extends BaseLogger {
$print("\n");
}
#elseif js
untyped js.Boot.__trace(v, infos);
if (js.Syntax.typeof(untyped console) != "undefined" && (untyped console).log != null)
(untyped console).log(v);
//untyped js.Boot.__trace(v, infos);
#elseif android
haxework.log.AndroidLog.write(3, "", ConstCharStar.fromString(Std.string(v)));
#elseif (php && php7)

2017
src/main/yaml/Parser.hx Normal file

File diff suppressed because it is too large Load Diff

638
src/main/yaml/Renderer.hx Normal file
View File

@@ -0,0 +1,638 @@
package yaml;
import Type;
import yaml.util.StringMap;
import yaml.util.IntMap;
import haxe.Utf8;
import haxe.io.Bytes;
import yaml.YamlType;
import yaml.schema.DefaultSchema;
import yaml.schema.SafeSchema;
import yaml.util.Strings;
import yaml.util.Ints;
import yaml.YamlException;
class RenderOptions
{
public var schema:Schema;
public var indent:Int;
public var flow:Int;
public var styles:StringMap<String>;
public function new(?schema:Schema, ?styles:StringMap<String>)
{
this.schema = (schema != null) ? schema : new DefaultSchema();
this.styles = (styles != null) ? styles : new StringMap();
this.indent = 2;
this.flow = -1;
}
public function setSchema(schema:Schema):RenderOptions
{
this.schema = schema;
return this;
}
public function setFlowLevel(level:Int):RenderOptions
{
this.flow = level;
return this;
}
/**
The indentation level. Default is 2.
*/
public function setIndent(indent:Int):RenderOptions
{
this.indent = indent;
return this;
}
public function setStyle(name:String, value:String):RenderOptions
{
styles.set(name, value);
return this;
}
}
class Renderer
{
/**
Utility method to create RenderOptions for configuring a Renderer instance.
*/
public static function options():RenderOptions
{
return new RenderOptions();
}
var schema:Schema;
var indent:Int;
var flowLevel:Int;
var styleMap:StringMap<Dynamic>;
var implicitTypes:Array<AnyYamlType>;
var explicitTypes:Array<AnyYamlType>;
var kind:String;
var tag:String;
var result:Dynamic;
public function new()
{}
public function safeRender(input:Dynamic, options:RenderOptions)
{
options.schema = new SafeSchema();
return render(input, options);
}
public function render(input:Dynamic, options:RenderOptions)
{
schema = options.schema;
indent = Std.int(Math.max(1, options.indent));
flowLevel = options.flow;
styleMap = compileStyleMap(schema, options.styles);
implicitTypes = schema.compiledImplicit;
explicitTypes = schema.compiledExplicit;
writeNode(0, input, true, true);
return result + '\n';
}
function generateNextLine(level:Int)
{
return '\n' + Strings.repeat(' ', indent * level);
}
function testImplicitResolving(object:Dynamic)
{
for (type in implicitTypes)
{
try
{
if (!type.loader.skip)
{
type.resolve(object, false);
return true;
}
}
catch(e:ResolveTypeException) {}
}
return false;
}
function writeScalar(object:String)
{
#if sys
object = Utf8.encode(object);
#end
var isQuoted = false;
var checkpoint = 0;
var position = -1;
result = '';
if (0 == object.length ||
CHAR_SPACE == Utf8.charCodeAt(object, 0) ||
CHAR_SPACE == Utf8.charCodeAt(object, Utf8.length(object) - 1))
{
isQuoted = true;
}
var length = Utf8.length(object);
while (++position < length)
{
var character = Utf8.charCodeAt(object, position);
if (!isQuoted)
{
if (CHAR_TAB == character ||
CHAR_LINE_FEED == character ||
CHAR_CARRIAGE_RETURN == character ||
CHAR_COMMA == character ||
CHAR_LEFT_SQUARE_BRACKET == character ||
CHAR_RIGHT_SQUARE_BRACKET == character ||
CHAR_LEFT_CURLY_BRACKET == character ||
CHAR_RIGHT_CURLY_BRACKET == character ||
CHAR_SHARP == character ||
CHAR_AMPERSAND == character ||
CHAR_ASTERISK == character ||
CHAR_EXCLAMATION == character ||
CHAR_VERTICAL_LINE == character ||
CHAR_GREATER_THAN == character ||
CHAR_SINGLE_QUOTE == character ||
CHAR_DOUBLE_QUOTE == character ||
CHAR_PERCENT == character ||
CHAR_COMMERCIAL_AT == character ||
CHAR_GRAVE_ACCENT == character ||
CHAR_QUESTION == character ||
CHAR_COLON == character ||
CHAR_MINUS == character)
{
isQuoted = true;
}
}
if (ESCAPE_SEQUENCES.exists(character) ||
!((0x00020 <= character && character <= 0x00007E) ||
(0x00085 == character) ||
(0x000A0 <= character && character <= 0x00D7FF) ||
(0x0E000 <= character && character <= 0x00FFFD) ||
(0x10000 <= character && character <= 0x10FFFF)))
{
result += yaml.util.Utf8.substring(object, checkpoint, position);
if (ESCAPE_SEQUENCES.exists(character))
{
result += ESCAPE_SEQUENCES.get(character);
}
else
{
result += encodeHex(character);
}
checkpoint = position + 1;
isQuoted = true;
}
}
if (checkpoint < position)
{
result += yaml.util.Utf8.substring(object, checkpoint, position);
}
if (!isQuoted && testImplicitResolving(result))
{
isQuoted = true;
}
if (isQuoted)
{
result = '"' + result + '"';
}
#if sys
result = Utf8.decode(result);
#end
}
function writeFlowSequence(level:Int, object:Array<Dynamic>)
{
var _result = '';
var _tag = tag;
for (index in 0...object.length)
{
if (0 != index)
_result += ', ';
writeNode(level, object[index], false, false);
_result += result;
}
tag = _tag;
result = '[' + _result + ']';
}
function writeBlockSequence(level:Int, object:Array<Dynamic>, compact:Bool)
{
var _result = '';
var _tag = tag;
for (index in 0...object.length)
{
if (!compact || 0 != index)
_result += generateNextLine(level);
writeNode(level + 1, object[index], true, true);
_result += '- ' + result;
}
tag = _tag;
result = _result;
}
function writeFlowMapping(level:Int, object:Dynamic)
{
if (Type.typeof(object) == ValueType.TObject)
writeObjectFlowMapping(level, object);
else
writeMapFlowMapping(level, object);
}
function writeObjectFlowMapping(level:Int, object:Dynamic)
{
var _result = '';
var _tag = tag;
var index = 0;
var objectKey;
for (objectKey in Reflect.fields(object))
{
if (0 != index++)
_result += ', ';
var objectValue = Reflect.field(object, objectKey);
writeNode(level, objectKey, false, false);
if (result.length > 1024)
_result += '? ';
_result += result + ': ';
writeNode(level, objectValue, false, false);
_result += result;
}
tag = _tag;
result = '{' + _result + '}';
}
#if haxe3
function writeMapFlowMapping<K,V>(level:Int, object:Map<K,V>)
#else
function writeMapFlowMapping(level:Int, object:Dynamic)
#end
{
var _result = '';
var _tag = tag;
var index = 0;
var objectKey;
var keys:Iterator<Dynamic> = object.keys();
for (objectKey in keys)
{
if (0 != index++)
_result += ', ';
var objectValue = object.get(objectKey);
writeNode(level, objectKey, false, false);
if (result.length > 1024)
_result += '? ';
_result += result + ': ';
writeNode(level, objectValue, false, false);
_result += result;
}
tag = _tag;
result = '{' + _result + '}';
}
function writeBlockMapping(level:Int, object:Dynamic, compact:Bool)
{
if (Type.typeof(object) == ValueType.TObject)
writeObjectBlockMapping(level, object, compact);
else
writeMapBlockMapping(level, object, compact);
}
function writeObjectBlockMapping(level:Int, object:Dynamic, compact:Bool)
{
var _result = '';
var _tag = tag;
var index = 0;
for (objectKey in Reflect.fields(object))
{
if (!compact || 0 != index++)
_result += generateNextLine(level);
var objectValue = Reflect.field(object, objectKey);
writeNode(level + 1, objectKey, true, true);
var explicitPair = (null != tag && '?' != tag && result.length <= 1024);
if (explicitPair)
_result += '? ';
_result += result;
if (explicitPair)
_result += generateNextLine(level);
writeNode(level + 1, objectValue, true, explicitPair);
_result += ': ' + result;
}
tag = _tag;
result = _result;
}
#if haxe3
function writeMapBlockMapping<K,V>(level:Int, object:Map<K,V>, compact:Bool)
#else
function writeMapBlockMapping(level:Int, object:Dynamic, compact:Bool)
#end
{
var _result = '';
var _tag = tag;
var index = 0;
var keys:Iterator<Dynamic> = object.keys();
for (objectKey in keys)
{
if (!compact || 0 != index++)
_result += generateNextLine(level);
var objectValue = object.get(objectKey);
writeNode(level + 1, objectKey, true, true);
var explicitPair = (null != tag && '?' != tag && result.length <= 1024);
if (explicitPair)
_result += '? ';
_result += result;
if (explicitPair)
_result += generateNextLine(level);
writeNode(level + 1, objectValue, true, explicitPair);
_result += ': ' + result;
}
tag = _tag;
result = _result;
}
function detectType(object:Dynamic, explicit:Bool)
{
var _result:Dynamic = null;
var typeList:Array<AnyYamlType> = explicit ? explicitTypes : implicitTypes;
var style:String;
kind = kindOf(object);
for (type in typeList)
{
if ((null != type.dumper) &&
type.dumper.skip != true &&
(null == type.dumper.kind || kind == type.dumper.kind) &&
(null == type.dumper.instanceOf || Std.is(object, type.dumper.instanceOf) &&
(null == type.dumper.predicate || type.dumper.predicate(object))))
{
tag = explicit ? type.tag : '?';
if (styleMap.exists(type.tag))
style = styleMap.get(type.tag);
else
style = type.dumper.defaultStyle;
var success = true;
try
{
_result = type.represent(object, style);
}
catch (e:RepresentTypeException)
{
success = false;
}
if (success)
{
kind = kindOf(_result);
result = _result;
}
else
{
if (explicit)
throw new YamlException('cannot represent an object of !<' + type.tag + '> type');
else
continue;
}
return true;
}
}
return false;
}
function writeNode(level:Int, object:Dynamic, block:Bool, compact:Bool)
{
tag = null;
result = object;
if (!detectType(object, false))
detectType(object, true);
if (block)
block = (0 > flowLevel || flowLevel > level);
if ((null != tag && '?' != tag) || (2 != indent && level > 0))
compact = false;
if ('object' == kind)
{
var empty = (Type.typeof(object) == ValueType.TObject) ? (Reflect.fields(object).length == 0) : Lambda.empty(object);
if (block && !empty)
{
writeBlockMapping(level, object, compact);
}
else
{
writeFlowMapping(level, object);
}
}
else if ('array' == kind)
{
if (block && (0 != result.length))
{
writeBlockSequence(level, result, compact);
}
else
{
writeFlowSequence(level, result);
}
}
else if ('string' == kind)
{
if ('?' != tag)
{
writeScalar(result);
}
}
else
{
throw new YamlException('unacceptabe kind of an object to dump (' + kind + ')');
}
if (null != tag && '?' != tag)
{
result = '!<' + tag + '> ' + result;
}
}
public function kindOf(object:Dynamic)
{
var kind = Type.typeof(object);
return switch (Type.typeof(object))
{
case TNull: "null";
case TInt: "integer";
case TFloat: "float";
case TBool: "boolean";
case TObject:
if (Std.is(object, Array)) "array";
else "object";
case TFunction: "function";
case TClass(c):
if (c == String) "string";
else if (c == Array) "array";
else if (c == Bytes) "binary";
else "object";
case TEnum(_): "enum";
case TUnknown: "unknown";
}
}
static function compileStyleMap(schema:Schema, map:StringMap<Dynamic>):StringMap<Dynamic>
{
if (null == map)
return new StringMap();
var result = new StringMap<Dynamic>();
for (tag in map.keys())
{
var style = Std.string(map.get(tag));
if (0 == tag.indexOf('!!'))
tag = 'tag:yaml.org,2002:' + tag.substring(2);
var type = schema.compiledTypeMap.get(tag);
if (type != null && type.dumper != null)
{
if (type.dumper.styleAliases.exists(style))
{
style = type.dumper.styleAliases.get(style);
}
}
result.set(tag, style);
}
return result;
}
static function encodeHex(charCode:Int)
{
var handle:String;
var length:Int;
var str = Ints.toString(charCode, 16).toUpperCase();
if (charCode <= 0xFF)
{
handle = 'x';
length = 2;
}
else if (charCode <= 0xFFFF)
{
handle = 'u';
length = 4;
}
else if (charCode <= 0xFFFFFFFF)
{
handle = 'U';
length = 8;
}
else
{
throw new YamlException('code point within a string may not be greater than 0xFFFFFFFF');
}
return '\\' + handle + Strings.repeat('0', length - str.length) + str;
}
static inline var CHAR_TAB = 0x09; /* Tab */
static inline var CHAR_LINE_FEED = 0x0A; /* LF */
static inline var CHAR_CARRIAGE_RETURN = 0x0D; /* CR */
static inline var CHAR_SPACE = 0x20; /* Space */
static inline var CHAR_EXCLAMATION = 0x21; /* ! */
static inline var CHAR_DOUBLE_QUOTE = 0x22; /* " */
static inline var CHAR_SHARP = 0x23; /* # */
static inline var CHAR_PERCENT = 0x25; /* % */
static inline var CHAR_AMPERSAND = 0x26; /* & */
static inline var CHAR_SINGLE_QUOTE = 0x27; /* ' */
static inline var CHAR_ASTERISK = 0x2A; /* * */
static inline var CHAR_COMMA = 0x2C; /* , */
static inline var CHAR_MINUS = 0x2D; /* - */
static inline var CHAR_COLON = 0x3A; /* : */
static inline var CHAR_GREATER_THAN = 0x3E; /* > */
static inline var CHAR_QUESTION = 0x3F; /* ? */
static inline var CHAR_COMMERCIAL_AT = 0x40; /* @ */
static inline var CHAR_LEFT_SQUARE_BRACKET = 0x5B; /* [ */
static inline var CHAR_RIGHT_SQUARE_BRACKET = 0x5D; /* ] */
static inline var CHAR_GRAVE_ACCENT = 0x60; /* ` */
static inline var CHAR_LEFT_CURLY_BRACKET = 0x7B; /* { */
static inline var CHAR_VERTICAL_LINE = 0x7C; /* | */
static inline var CHAR_RIGHT_CURLY_BRACKET = 0x7D; /* } */
static var HEX_VALUES = "0123456789ABCDEF";
static var ESCAPE_SEQUENCES:IntMap<String> =
{
var hash = new IntMap<String>();
hash.set(0x00, '\\0');
hash.set(0x07, '\\a');
hash.set(0x08, '\\b');
hash.set(0x09, '\\t');
hash.set(0x0A, '\\n');
hash.set(0x0B, '\\v');
hash.set(0x0C, '\\f');
hash.set(0x0D, '\\r');
hash.set(0x1B, '\\e');
hash.set(0x22, '\\"');
hash.set(0x5C, '\\\\');
hash.set(0x85, '\\N');
hash.set(0xA0, '\\_');
hash.set(0x2028, '\\L');
hash.set(0x2029, '\\P');
hash;
};
}

95
src/main/yaml/Schema.hx Normal file
View File

@@ -0,0 +1,95 @@
package yaml;
import yaml.util.StringMap;
import yaml.YamlType;
class Schema
{
public static var DEFAULT:Schema;
public var compiledImplicit:Array<AnyYamlType>;
public var compiledExplicit:Array<AnyYamlType>;
public var compiledTypeMap:StringMap<AnyYamlType>;
public var implicit:Array<AnyYamlType>;
public var explicit:Array<AnyYamlType>;
public var include:Array<Schema>;
public function new(include:Array<Schema>, explicit:Array<AnyYamlType>, ?implicit:Array<AnyYamlType>)
{
this.include = (include == null) ? [] : include;
this.implicit = (implicit == null) ? [] : implicit;
this.explicit = (explicit == null) ? [] : explicit;
for (type in this.implicit)
{
if (null != type.loader && 'string' != type.loader.kind)
{
throw new YamlException('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.');
}
}
this.compiledImplicit = compileList(this, 'implicit', []);
this.compiledExplicit = compileList(this, 'explicit', []);
this.compiledTypeMap = compileMap([this.compiledImplicit, this.compiledExplicit]);
}
public static function create(types:Array<AnyYamlType>, ?schemas:Array<Schema>)
{
if (schemas == null)
schemas = [DEFAULT];
else if (schemas.length == 0)
schemas.push(DEFAULT);
return new Schema(schemas, types);
}
public static function compileList(schema:Schema, name:String, result:Array<AnyYamlType>)
{
var exclude = [];
for (includedSchema in schema.include)
{
result = compileList(includedSchema, name, result);
}
var types:Array<AnyYamlType> = switch (name)
{
case "implicit": schema.implicit;
case "explicit": schema.explicit;
default: throw new YamlException("unknown type list type: " + name);
}
for (currenYamlType in types)
{
for (previousIndex in 0...result.length)
{
var previousType = result[previousIndex];
if (previousType.tag == currenYamlType.tag)
{
exclude.push(previousIndex);
}
}
result.push(currenYamlType);
}
var filteredResult:Array<AnyYamlType> = [];
for (i in 0...result.length)
if (!Lambda.has(exclude, i))
filteredResult.push(result[i]);
return filteredResult;
}
public static function compileMap(list:Array<Array<AnyYamlType>>):StringMap<AnyYamlType>
{
var result = new StringMap<AnyYamlType>();
for (member in list)
for (type in member)
result.set(type.tag, type);
return result;
}
}

69
src/main/yaml/Yaml.hx Normal file
View File

@@ -0,0 +1,69 @@
package yaml;
import yaml.Renderer;
import yaml.Parser;
/**
Facade for common YAML processing operations.
*/
class Yaml
{
/**
Parse a yaml document into object form.
@param document The yaml document to parse.
@param ?options Parsing options (optional).
@return The parsed yaml document in object form.
*/
public static function parse(document:String, ?options:ParserOptions):Dynamic
{
if (options == null) options = new ParserOptions();
return new Parser().parse(document, options);
}
#if sys
/**
Read a yaml document from disk and parse into object form.
@param filePath The path to read the yaml file.
@param ?options Parsing options (optional).
@return The parsed yaml document in object form.
*/
public static function read(filePath:String, ?options:ParserOptions):Dynamic
{
return parse(sys.io.File.getContent(filePath), options);
}
#end
/**
Render a yaml object graph as a yaml text document.
@param data The root object to render.
@param ?options Rendering options (optional).
@return The rendered yaml document.
*/
public static function render(data:Dynamic, ?options:RenderOptions):String
{
if (options == null) options = new RenderOptions();
return new Renderer().render(data, options);
}
#if sys
/**
Render an object graph as a yaml text document and write it to disk.
@param filePath The path to write the yaml document.
@param data The root object to render.
@param ?options Rendering options (optional).
*/
public static function write(filePath:String, data:Dynamic, ?options:RenderOptions):Void
{
sys.io.File.saveContent(filePath, render(data, options));
}
#end
private function new() {}
}

View File

@@ -0,0 +1,31 @@
package yaml;
import haxe.PosInfos;
class YamlException
{
public var name(get, null):String;
function get_name():String { return name; }
public var message(get, null):String;
function get_message():String { return message; }
public var cause(default, null):Dynamic;
public var info(default, null):PosInfos;
public function new(?message:String="", ?cause:Dynamic = null, ?info:PosInfos)
{
this.name = Type.getClassName(Type.getClass(this));
this.message = message;
this.cause = cause;
this.info = info;
}
public function toString():String
{
var str:String = name + ": " + message;
if (info != null)
str += " at " + info.className + "#" + info.methodName + " (" + info.lineNumber + ")";
return str;
}
}

112
src/main/yaml/YamlType.hx Normal file
View File

@@ -0,0 +1,112 @@
package yaml;
import yaml.util.StringMap;
import haxe.PosInfos;
typedef POptions =
{
?kind:String,
?skip:Bool
}
typedef ROptions =
{
?kind:String,
?defaultStyle:String,
?instanceOf:Class<Dynamic>,
?predicate:Dynamic->Bool,
?styleAliases:StringMap<String>,
?skip:Bool
}
typedef AnyYamlType = YamlType<Dynamic, Dynamic>;
typedef StringYamlType<T> = YamlType<T, String>;
class YamlType<T, D>
{
public var tag:String;
public var loader:POptions;
public var dumper:ROptions;
public function new(tag:String, loaderOptions:POptions, dumperOptions:ROptions)
{
if (loaderOptions == null && dumperOptions == null)
throw new YamlException('Incomplete YAML type definition. "loader" or "dumper" setting must be specified.');
this.tag = tag;
this.loader = loaderOptions;
this.dumper = dumperOptions;
if (loaderOptions != null && !loaderOptions.skip)
validateLoaderOptions();
if (dumperOptions != null && !dumperOptions.skip)
validateDumperOptions();
}
public function resolve(object:D, ?usingMaps:Bool = true, ?explicit:Bool = false):T
{
cantResolveType();
return null;
}
public function represent(object:T, ?style:String):String
{
cantRepresentType();
return null;
}
function cantResolveType(?info:PosInfos):Dynamic
{
throw new ResolveTypeException("", null, info);
return null;
}
function cantRepresentType(?info:PosInfos):Dynamic
{
throw new RepresentTypeException("", null, info);
return null;
}
function validateLoaderOptions()
{
if (loader.skip != true && 'string' != loader.kind && 'array' != loader.kind && 'object' != loader.kind)
{
throw new YamlException('Unacceptable "kind" setting of a type loader: ' + loader.kind);
}
}
function validateDumperOptions()
{
if (dumper.skip != true &&
'undefined' != dumper.kind &&
'null' != dumper.kind &&
'boolean' != dumper.kind &&
'integer' != dumper.kind &&
'float' != dumper.kind &&
'string' != dumper.kind &&
'array' != dumper.kind &&
'object' != dumper.kind &&
'binary' != dumper.kind &&
'function' != dumper.kind)
{
throw new YamlException('Unacceptable "kind" setting of a type dumper: ' + dumper.kind);
}
}
}
class ResolveTypeException extends YamlException
{
public function new(?message:String="", ?cause:Dynamic = null, ?info:PosInfos)
{
super(message, cause, info);
}
}
class RepresentTypeException extends YamlException
{
public function new(?message:String="", ?cause:Dynamic = null, ?info:PosInfos)
{
super(message, cause, info);
}
}

View File

@@ -0,0 +1,11 @@
package yaml.schema;
import yaml.Schema;
class DefaultSchema extends Schema
{
public function new()
{
super([new SafeSchema()], null);//[new TRegex(), new TFunction()]);
}
}

View File

@@ -0,0 +1,14 @@
package yaml.schema;
import yaml.type.YSeq;
import yaml.type.YMap;
import yaml.type.YString;
import yaml.Schema;
class MinimalSchema extends Schema
{
public function new()
{
super([], [new YString(), new YSeq(), new YMap()]);
}
}

View File

@@ -0,0 +1,24 @@
package yaml.schema;
import yaml.type.YOmap;
import yaml.type.YMerge;
import yaml.type.YTimestamp;
import yaml.type.YSet;
import yaml.type.YPairs;
import yaml.type.YInt;
import yaml.type.YNull;
import yaml.schema.MinimalSchema;
import yaml.type.YFloat;
import yaml.type.YBool;
import yaml.type.YBinary;
import yaml.Schema;
class SafeSchema extends Schema
{
public function new()
{
super([new MinimalSchema()],
[new YBinary(), new YOmap(), new YPairs(), new YSet()],
[new YNull(), new YBool(), new YInt(), new YFloat(), new YTimestamp(), new YMerge()]);
}
}

View File

@@ -0,0 +1,119 @@
package yaml.type;
import haxe.io.Bytes;
import haxe.Utf8;
import yaml.YamlType;
class YBinary extends yaml.StringYamlType<Bytes>
{
static inline var BASE64_PADDING_CODE = 0x3D;
static inline var BASE64_PADDING_CHAR = '=';
static var BASE64_BINTABLE = [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
];
static var BASE64_CHARTABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
public function new()
{
super('tag:yaml.org,2002:binary', {kind:"string"}, {kind:"binary", instanceOf:Bytes});
}
override public function resolve(object:String, ?usingMaps:Bool = true, ?explicit:Bool):Bytes
{
var length = Utf8.length(object);
var idx = 0;
var result = [];
var leftbits = 0; // number of bits decoded, but yet to be appended
var leftdata = 0; // bits decoded, but yet to be appended
// Convert one by one.
for (idx in 0...length)
{
var code = Utf8.charCodeAt(object, idx);
var value = BASE64_BINTABLE[code & 0x7F];
// Skip LF(NL) || CR
if (0x0A != code && 0x0D != code)
{
// Fail on illegal characters
if (-1 == value)
return cantResolveType();
// Collect data into leftdata, update bitcount
leftdata = (leftdata << 6) | value;
leftbits += 6;
// If we have 8 or more bits, append 8 bits to the result
if (leftbits >= 8) {
leftbits -= 8;
// Append if not padding.
if (BASE64_PADDING_CODE != code) {
result.push((leftdata >> leftbits) & 0xFF);
}
leftdata &= (1 << leftbits) - 1;
}
}
}
// If there are any bits left, the base64 string was corrupted
if (leftbits != 0)
cantResolveType();
var bytes = Bytes.alloc(result.length);
for (i in 0...result.length)
bytes.set(i, result[i]);
return bytes;
}
override public function represent(object:Bytes, ?style:String):String
{
var result = '';
var index = 0;
var max = object.length - 2;
// Convert every three bytes to 4 ASCII characters.
while (index < max)
{
result += BASE64_CHARTABLE[object.get(index + 0) >> 2];
result += BASE64_CHARTABLE[((object.get(index + 0) & 0x03) << 4) + (object.get(index + 1) >> 4)];
result += BASE64_CHARTABLE[((object.get(index + 1) & 0x0F) << 2) + (object.get(index + 2) >> 6)];
result += BASE64_CHARTABLE[object.get(index + 2) & 0x3F];
index += 3;
}
var rest = object.length % 3;
// Convert the remaining 1 or 2 bytes, padding out to 4 characters.
if (0 != rest)
{
index = object.length - rest;
result += BASE64_CHARTABLE[object.get(index + 0) >> 2];
if (2 == rest)
{
result += BASE64_CHARTABLE[((object.get(index + 0) & 0x03) << 4) + (object.get(index + 1) >> 4)];
result += BASE64_CHARTABLE[(object.get(index + 1) & 0x0F) << 2];
result += BASE64_PADDING_CHAR;
}
else
{
result += BASE64_CHARTABLE[(object.get(index + 0) & 0x03) << 4];
result += BASE64_PADDING_CODE + BASE64_PADDING_CHAR;
}
}
return result;
}
}

View File

@@ -0,0 +1,92 @@
package yaml.type;
import yaml.util.StringMap;
import yaml.YamlException;
import yaml.YamlType;
class YBool extends StringYamlType<Bool>
{
static var YAML_IMPLICIT_BOOLEAN_MAP:StringMap<Bool> =
{
var hash = new StringMap<Bool>();
hash.set("true", true);
hash.set("True", true);
hash.set("TRUE", true);
hash.set("false", false);
hash.set("False", false);
hash.set("FALSE", false);
hash;
};
static var YAML_EXPLICIT_BOOLEAN_MAP =
{
var hash = new StringMap<Bool>();
hash.set("true", true);
hash.set("True", true);
hash.set("TRUE", true);
hash.set("false", false);
hash.set("False", false);
hash.set("FALSE", false);
hash.set("y", true);
hash.set("Y", true);
hash.set("yes", true);
hash.set("Yes", true);
hash.set("YES", true);
hash.set("n", false);
hash.set("N", false);
hash.set("no", false);
hash.set("No", false);
hash.set("NO", false);
hash.set("on", true);
hash.set("On", true);
hash.set("ON", true);
hash.set("off", false);
hash.set("Off", false);
hash.set("OFF", false);
hash;
};
public function new()
{
super('tag:yaml.org,2002:bool', {kind:"string"}, {kind:"boolean", defaultStyle:"lowercase"});
}
override public function resolve(object:String, ?usingMaps:Bool = true, ?explicit:Bool):Bool
{
if (explicit)
{
if (YAML_EXPLICIT_BOOLEAN_MAP.exists(object))
{
return YAML_EXPLICIT_BOOLEAN_MAP.get(object);
}
else
{
return cantResolveType();
}
}
else
{
if (YAML_IMPLICIT_BOOLEAN_MAP.exists(object))
{
return YAML_IMPLICIT_BOOLEAN_MAP.get(object);
}
else
{
return cantResolveType();
}
}
}
override public function represent(object:Bool, ?style:String):String
{
return switch (style)
{
case "uppercase": object ? 'TRUE' : 'FALSE';
case "lowercase": object ? 'true' : 'false';
case "camelcase": object ? 'True' : 'False';
default:
throw new YamlException("Style not supported: " + style);
null;
}
}
}

View File

@@ -0,0 +1,107 @@
package yaml.type;
import yaml.YamlType;
import yaml.util.Floats;
class YFloat extends yaml.StringYamlType<Null<Float>>
{
static var YAML_FLOAT_PATTERN = new EReg(
'^(?:[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+][0-9]+)?' +
'|\\.[0-9_]+(?:[eE][-+][0-9]+)?' +
'|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*' +
'|[-+]?\\.(?:inf|Inf|INF)' +
'|\\.(?:nan|NaN|NAN))$', "iu");
public function new()
{
super('tag:yaml.org,2002:float', {kind:"string"}, {kind:"float", defaultStyle:"lowercase"});
}
override public function resolve(object:String, ?usingMaps:Bool = true, ?explicit:Bool):Null<Float>
{
if (!YAML_FLOAT_PATTERN.match(object))
cantResolveType();
var value:String = StringTools.replace(object, '_', '').toLowerCase();
var sign = ('-' == value.charAt(0)) ? -1 : 1;
if (0 <= '+-'.indexOf(value.charAt(0)))
{
value = value.substr(1);
}
if ('.inf' == value)
{
return (1 == sign) ? Math.POSITIVE_INFINITY : Math.NEGATIVE_INFINITY;
}
else if ('.nan' == value)
{
return Math.NaN;
}
else if (0 <= value.indexOf(':'))
{
var digits:Array<Float> = [];
for (v in value.split(':'))
{
digits.unshift(Std.parseFloat(v));
}
var v = 0.0;
var base = 1;
for (d in digits)
{
v += d * base;
base *= 60;
}
return sign * v;
}
else
{
return sign * Std.parseFloat(value);
}
}
override public function represent(object:Null<Float>, ?style:String):String
{
if (Math.isNaN(object))
{
return switch (style)
{
case 'lowercase': '.nan';
case 'uppercase': '.NAN';
case 'camelcase': '.NaN';
default: ".nan";
}
}
else if (Math.POSITIVE_INFINITY == object)
{
return switch (style)
{
case 'lowercase': '.inf';
case 'uppercase': '.INF';
case 'camelcase': '.Inf';
default: ".inf";
}
}
else if (Math.NEGATIVE_INFINITY == object)
{
return switch (style)
{
case 'lowercase': '-.inf';
case 'uppercase': '-.INF';
case 'camelcase': '-.Inf';
default: "-.inf";
}
}
else
{
return Floats.toString(object);
}
}
}

103
src/main/yaml/type/YInt.hx Normal file
View File

@@ -0,0 +1,103 @@
package yaml.type;
import yaml.YamlException;
import yaml.util.StringMap;
import yaml.util.Ints;
import yaml.YamlType;
class YInt extends yaml.StringYamlType<Null<Int>>
{
static var YAML_INTEGER_PATTERN = new EReg(
'^(?:[-+]?0b[0-1_]+' +
'|[-+]?0[0-7_]+' +
'|[-+]?(?:0|[1-9][0-9_]*)' +
'|[-+]?0x[0-9a-fA-F_]+' +
'|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$', "iu");
public function new()
{
super('tag:yaml.org,2002:int', {kind:"string"}, {kind:"integer", defaultStyle:"decimal", styleAliases:createStyleAliases()});
}
function createStyleAliases()
{
var styleAliases = new StringMap<String>();
styleAliases.set('bin', "binary");
styleAliases.set('2', "binary");
styleAliases.set('oct', "octal");
styleAliases.set('8', "octal");
styleAliases.set('dec', "decimal");
styleAliases.set('hex', "hexadecimal");
styleAliases.set('16', "hexadecimal");
return styleAliases;
}
override public function resolve(object:String, ?usingMaps:Bool = true, ?explicit:Bool):Null<Int>
{
if (!YAML_INTEGER_PATTERN.match(object))
cantResolveType();
var value = StringTools.replace(object, '_', '').toLowerCase();
var sign = ('-' == value.charAt(0)) ? -1 : 1;
var digits = [];
if (0 <= '+-'.indexOf(value.charAt(0)))
{
value = value.substr(1);
}
if ('0' == value)
{
return 0;
}
else if (value.indexOf("0b") == 0)
{
return sign * Ints.parseInt(value.substr(2), 2);
}
else if (value.indexOf("0x") == 0)
{
return sign * Ints.parseInt(value, 16);
}
else if (value.indexOf("0") == 0)
{
return sign * Ints.parseInt(value, 8);
}
else if (0 <= value.indexOf(':')) // base 60
{
for (v in value.split(':'))
{
digits.unshift(Ints.parseInt(v, 10));
}
var result = 0;
var base = 1;
for (d in digits)
{
result += (d * base);
base *= 60;
}
return sign * result;
}
else
{
return sign * Ints.parseInt(value, 10);
}
}
override public function represent(object:Null<Int>, ?style:String):String
{
return switch (style)
{
case "binary": '0b' + Ints.toString(object, 2);
case "octal": '0' + Ints.toString(object, 8);
case "decimal": Ints.toString(object, 10);
case "hexadecimal": '0x' + Ints.toString(object, 16);
default:
throw new YamlException("Style not supported: " + style);
null;
}
}
}

View File

@@ -0,0 +1,12 @@
package yaml.type;
import yaml.util.ObjectMap;
import yaml.YamlType;
class YMap extends yaml.StringYamlType<Dynamic>
{
public function new()
{
super('tag:yaml.org,2002:map', {kind:"object", skip:true}, {skip:true});
}
}

View File

@@ -0,0 +1,21 @@
package yaml.type;
import yaml.YamlType;
class YMerge extends yaml.StringYamlType<String>
{
public function new()
{
super('tag:yaml.org,2002:merge', {kind:"string"}, {skip:true});
}
override public function resolve(object:String, ?usingMaps:Bool = true, ?explicit:Bool):String
{
return ('<<' == object) ? object : cantResolveType();
}
override public function represent(object:String, ?style:String):String
{
return null;
}
}

View File

@@ -0,0 +1,38 @@
package yaml.type;
import yaml.util.StringMap;
import yaml.YamlType;
class YNull extends yaml.StringYamlType<Dynamic>
{
static var YAML_NULL_MAP:StringMap<Bool> = {
var hash = new StringMap<Bool>();
hash.set('~', true);
hash.set('null', true);
hash.set('Null', true);
hash.set('NULL', true);
hash;
};
public function new()
{
super('tag:yaml.org,2002:null', {kind:"string"}, {kind:"null", defaultStyle:"lowercase"});
}
override public function resolve(object:String, ?usingMaps:Bool = true, ?explicit:Bool = false):Dynamic
{
return YAML_NULL_MAP.exists(object) ? null : cantResolveType();
}
override public function represent(object:Dynamic, ?style:String):String
{
return switch (style)
{
case "canonical": "~";
case "lowercase": "null";
case "uppercase": "NULL";
case "camelcase": "Null";
default: "~";
}
}
}

View File

@@ -0,0 +1,76 @@
package yaml.type;
import yaml.util.StringMap;
import yaml.util.ObjectMap;
import Type;
class YOmap extends yaml.YamlType<Array<Dynamic>, Array<Dynamic>>
{
public function new()
{
super("tag:yaml.org,2002:omap", {kind:"array"}, {skip:true});
}
// For omap this method is only doing a validation that no duplicates are present and that
// each pair has only one key. It's not really 'resolving' the value...
override public function resolve(object:Array<Dynamic>, ?usingMaps:Bool = true, ?explicit:Bool = false):Array<Dynamic>
{
usingMaps ? validateOMap(cast object) : validateObjectOMap(object);
return object;
}
function validateOMap(object:Array<AnyObjectMap>)
{
var objectKeys = new ObjectMap<Dynamic, Dynamic>();
for (pair in object)
{
var pairHasKey = false;
var pairKey:Dynamic = null;
if (!Std.is(pair, AnyObjectMap))
cantResolveType();
for (key in pair.keys())
{
if (pairKey == null) pairKey = key;
else cantResolveType(); // can only have one key
}
if (pairKey == null) // must have a key
cantResolveType();
if (objectKeys.exists(pairKey))
cantResolveType(); // no duplicate keys allowed
else
objectKeys.set(pairKey, null);
}
return object;
}
function validateObjectOMap(object:Array<Dynamic>)
{
var objectKeys = new StringMap<Dynamic>();
for (pair in object)
{
var pairHasKey = false;
var pairKey:String = null;
if (Type.typeof(pair) != ValueType.TObject)
cantResolveType();
for (key in Reflect.fields(pair))
{
if (pairKey == null) pairKey = key;
else cantResolveType(); // can only have one key
}
if (pairKey == null) // must have a key
cantResolveType();
if (objectKeys.exists(pairKey)) cantResolveType(); // no duplicate keys allowed
else objectKeys.set(pairKey, null);
}
}
}

View File

@@ -0,0 +1,69 @@
package yaml.type;
import yaml.util.ObjectMap;
import yaml.YamlType;
import Type;
class YPairs extends YamlType<Array<Array<Dynamic>>, Array<Dynamic>>
{
public function new()
{
super("tag:yaml.org,2002:pairs", {kind:"array"}, {skip:true});
}
override public function resolve(object:Array<Dynamic>, ?usingMaps:Bool = true, ?explicit:Bool = false):Array<Array<Dynamic>>
{
if (usingMaps)
return resolveMapPair(cast object);
else
return resolveObjectPair(object);
}
function resolveMapPair(object:Array<AnyObjectMap>)
{
var result:Array<Array<Dynamic>> = [];
for (pair in object)
{
if (!Std.is(pair, AnyObjectMap))
cantResolveType();
var fieldCount = 0;
var keyPair:Dynamic = null;
for (key in pair.keys())
{
keyPair = key;
if (fieldCount++ > 1) break;
}
if (fieldCount != 1) // must have one key
cantResolveType();
result.push([keyPair, pair.get(keyPair)]);
}
return result;
}
function resolveObjectPair(object:Array<Dynamic>)
{
var result:Array<Array<Dynamic>> = [];
for (pair in object)
{
if (Type.typeof(pair) != ValueType.TObject)
cantResolveType();
var fieldCount = 0;
var keyPair:Dynamic = null;
for (key in Reflect.fields(pair))
{
keyPair = key;
if (fieldCount++ > 1) break;
}
if (fieldCount != 1) // must have one key
cantResolveType();
result.push([keyPair, Reflect.field(pair, keyPair)]);
}
return result;
}
}

View File

@@ -0,0 +1,11 @@
package yaml.type;
import yaml.YamlType;
class YSeq extends StringYamlType<Array<Dynamic>>
{
public function new()
{
super('tag:yaml.org,2002:seq', {kind:"array", skip:true}, {skip:true});
}
}

View File

@@ -0,0 +1,33 @@
package yaml.type;
import yaml.util.ObjectMap;
import yaml.YamlType;
class YSet extends YamlType<Dynamic, Dynamic>
{
public function new()
{
super('tag:yaml.org,2002:set', {kind:"object"}, {skip:true});
}
override public function resolve(object:Dynamic, ?usingMaps:Bool = true, ?explicit:Bool = false):Dynamic
{
// again this is just validation, not resolving anything.
usingMaps ? validateSet(object) : validateObjectSet(object);
return object;
}
function validateSet(object:AnyObjectMap)
{
for (key in object.keys())
if (object.get(key) != null)
cantResolveType();
}
function validateObjectSet(object:Dynamic)
{
for (key in Reflect.fields(object))
if (Reflect.field(object, key) != null)
cantResolveType();
}
}

View File

@@ -0,0 +1,11 @@
package yaml.type;
import yaml.YamlType;
class YString extends yaml.StringYamlType<String>
{
public function new()
{
super('tag:yaml.org,2002:str', {kind:"string", skip:true}, {skip:true});
}
}

View File

@@ -0,0 +1,135 @@
package yaml.type;
import yaml.YamlType;
//class TTimestamp extends StringYamlType<Date>
class YTimestamp extends YamlType<Date, String>
{
static var YAML_TIMESTAMP_REGEXP = new EReg(
'^([0-9][0-9][0-9][0-9])' + // [1] year
'-([0-9][0-9]?)' + // [2] month
'-([0-9][0-9]?)' + // [3] day
'(?:(?:[Tt]|[ \\t]+)' + // ...
'([0-9][0-9]?)' + // [4] hour
':([0-9][0-9])' + // [5] minute
':([0-9][0-9])' + // [6] second
'(?:\\.([0-9]*))?' + // [7] fraction
'(?:[ \\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tz_hour
'(?::([0-9][0-9]))?))?)?$', "iu"); // [11] tz_minute
public function new()
{
super('tag:yaml.org,2002:timestamp', {kind:"string"}, {kind:"object", instanceOf:Date});
}
override public function resolve(object:String, ?usingMaps:Bool = true, ?explicit:Bool = false):Date
{
if (!YAML_TIMESTAMP_REGEXP.match(object))
cantResolveType();
// match: [1] year [2] month [3] day
var year:Null<Int> = 0;
var month:Null<Int> = 0;
var day:Null<Int> = 0;
var hour:Null<Int> = 0;
var minute:Null<Int> = 0;
var second:Null<Int> = 0;
var fraction:Null<Int> = 0;
var delta:Null<Int> = 0;
try
{
year = Std.parseInt(YAML_TIMESTAMP_REGEXP.matched(1));
month = Std.parseInt(YAML_TIMESTAMP_REGEXP.matched(2)) - 1; // month starts with 0
day = Std.parseInt(YAML_TIMESTAMP_REGEXP.matched(3));
hour = Std.parseInt(YAML_TIMESTAMP_REGEXP.matched(4));
minute = Std.parseInt(YAML_TIMESTAMP_REGEXP.matched(5));
second = Std.parseInt(YAML_TIMESTAMP_REGEXP.matched(6));
// EReg.matched doesn't throw exception under neko for whatever reason so we need to
// check for null on each matched call
var matched = -1;
if (year == null) matched = year = 0;
if (month == null) matched = month = 0;
if (day == null) matched = day = 0;
if (hour == null) matched = hour = 0;
if (minute == null) matched = minute = 0;
if (second == null) matched = second = 0;
if (matched == 0)
throw "Nothing left to match";
var msecs = YAML_TIMESTAMP_REGEXP.matched(7);
if (msecs == null)
throw "Nothing left to match";
var f = msecs.substring(0, 3);
while (f.length < 3)
f += '0';
fraction = Std.parseInt(f);
if (YAML_TIMESTAMP_REGEXP.matched(9) != null)
{
var tz_hour:Null<Int> = Std.parseInt(YAML_TIMESTAMP_REGEXP.matched(10));
if (tz_hour == null)
throw "Nothing left to match";
var tz_minute:Null<Int> = 0;
try {
tz_minute = Std.parseInt(YAML_TIMESTAMP_REGEXP.matched(11));
if (tz_minute == null)
tz_minute = 0;
}
catch(e:Dynamic) {}
delta = (tz_hour * 60 + tz_minute) * 60000; // delta in mili-seconds
if ('-' == YAML_TIMESTAMP_REGEXP.matched(9))
delta = -delta;
}
}
catch(e:Dynamic) {}
#if (js || flash)
var stamp:Float = nativeDate().UTC(year, month, day, hour, minute, second, fraction);
if (delta != 0)
stamp = stamp - delta;
return Date.fromTime(stamp);
#else
trace("Warning: UTC dates are not supported under this target");
var date = new Date(year, month, day, hour, minute, second);
// if (delta != 0)
// stamp = stamp - delta;
return date;
#end
}
#if (flash || js)
static function nativeDate():Dynamic
{
#if flash9
return untyped __global__["Date"];
#elseif flash
return untyped _global["Date"];
#elseif js
return untyped __js__("Date");
#end
return null;
}
#end
override public function represent(object:Date, ?style:String):String
{
#if (flash || js)
return yaml.util.Dates.toISOString(object);
#else
trace("Warning: UTC dates are not supported under this target");
return object.toString();
#end
}
}

View File

@@ -0,0 +1,69 @@
/*
Copyright (c) 2012 Massive Interactive
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package yaml.util;
/**
Utility methods for working with dates
*/
class Dates
{
#if (flash || js)
static function getNativeDate():Dynamic
{
#if flash9
return untyped __global__["Date"];
#elseif flash
return untyped _global["Date"];
#elseif js
return untyped __js__("Date");
#end
return null;
}
/**
Converts a Date to UTC ISO-8601 compliant string.
Example: 2012-06-20T12:11:08.188Z
Currently only supported under JS, AS3 and AS2.
@param date the date to convert to utc iso string
@return ISO-8601 compliant timestamp
*/
public static function toISOString(date:Date):String
{
var NativeDate = getNativeDate();
var d = untyped __new__(NativeDate, date.getTime());
return d.getUTCFullYear() + '-'
+ StringTools.lpad("" + (d.getUTCMonth() + 1), "0", 2) + '-'
+ StringTools.lpad("" + d.getUTCDate(), "0", 2) + 'T'
+ StringTools.lpad("" + d.getUTCHours(), "0", 2) + ':'
+ StringTools.lpad("" + d.getUTCMinutes(), "0", 2) + ':'
+ StringTools.lpad("" + d.getUTCSeconds(), "0", 2) + '.'
+ StringTools.rpad(("" + (Floats.round(d.getUTCMilliseconds()/1000, 3))).substr(2, 5), "0", 3)
+ 'Z';
}
#end
}

View File

@@ -0,0 +1,69 @@
/*
Copyright (c) 2012 Massive Interactive
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package yaml.util;
/**
Utility methods for working with float values.
*/
class Floats
{
/**
Returns the string representation of a float.
Under cpp will convert large floats with a positive exponent (eg date.getTime()) to a decimal string.
e.g 1.347420344e+12 -> 1347420344000
*/
public static function toString(value:Float):String
{
#if cpp
var str = Std.string(value);
if (str.indexOf("e+") != -1)
{
var parts = str.split(".");
var exp = parts[1].split("e+");
str = parts[0] + StringTools.rpad(exp[0], "0", Std.parseInt(exp[1]));
}
return str;
#else
return Std.string(value);
#end
}
/**
Rounds a value to a specific decimal precision
Example:
<pre>
FloatUtil.round(1.6666667 3);//returns 1.667;
</pre>
@param value value to round
@param precision precision in decimals
@return the rounded value
*/
static public function round(value:Float, precision:Int):Float
{
value = value * Math.pow(10, precision);
return Math.round(value) / Math.pow(10, precision);
}
}

View File

@@ -0,0 +1,7 @@
package yaml.util;
#if haxe3
typedef IntMap<T> = haxe.ds.IntMap<T>;
#else
typedef IntMap<T> = IntHash<T>;
#end

167
src/main/yaml/util/Ints.hx Normal file
View File

@@ -0,0 +1,167 @@
/*
Copyright (c) 2012 Massive Interactive
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package yaml.util;
/**
Utility methods for working with int values.
*/
class Ints
{
// Base used for toString/parseInt conversions. Supporting base 2 to 36 for now as common standard.
static var BASE = "0123456789abcdefghijklmnopqrstuvwxyz";
/**
Returns the string representation of an integer value based on an optional radix (base).
A default base of 10 is used if none is supplied.
A base below 2 or above 36 will cause an exception.
@param value The integer value to return the string representation of.
@param radix An integer between 2 and 36 to be used as the base for conversion. Defaults to 10.
@return The string representation of the integer at the defined radix.
*/
public static function toString(value:Int, ?radix:Int = 10):String
{
if (radix < 2 || radix > BASE.length)
throw "Unsupported radix " + radix;
#if (js || flash)
return untyped value.toString(radix);
#else
if (radix == 10) return Std.string(value);
else if (value == 0) return '0';
else // do the conversion ourselves
{
var sign = "";
if (value < 0)
{
sign = '-';
value = -value;
}
var result = '';
while (value > 0)
{
result = BASE.charAt(value % radix) + result;
value = Std.int(value / radix);
}
return sign + result;
}
#end
}
/**
Parses a string and returns the decimal integer value it represents or `null` if no value could be parsed.
An optional radix can be supplied to indicate the base the value should be parsed under.
If no base is supplied a base of 10 will be assumed, unless the value begins with '0x', in which case
a base of 16 (hex) is assumed.
Parsing continues until complete or an invalid char is found. In that case the value parsed up until that point
is returned. e.g. `parseInt("44dae", 10)` returns `44`.
The reason for returning `null` instead of `Math.NaN` when parsing fails completely is that NaN is a Float which
would force the return type to also be Float. This would mean `var a:Int = parseInt(value)` would become invalid,
which is unintuitive. A return type of Dynamic would fix this on most systems, but not flash (avm2) which
will auto cast the Float to an Int meaning Math.isNaN test would always fail.
@param value The value to parse into an integer
@param radix The radix to base the conversion on. Default is 16 for string starting with '0x' otherwise 10.
@return The decimal integer value which the supplied string represents, or null if it could not be parsed.
*/
public static function parseInt(value:String, ?radix:Null<Int>):Null<Int>
{
if (radix != null && (radix < 2 || radix > BASE.length))
throw "Unsupported radix " + radix;
#if js
var v = untyped __js__("parseInt")(value, radix);
return (untyped __js__("isNaN")(v)) ? null : v;
#elseif flash9
if (radix == null) radix = 0;
var v = untyped __global__["parseInt"](value, radix);
return (untyped __global__["isNaN"](v)) ? null : v;
#elseif flash8
var v = _global["parseInt"](value, radix);
return _global["isNaN"](v) ? null : v;
#else // do the conversion ourselves
value = StringTools.trim(value).toLowerCase();
if (value.length == 0)
return null;
var s = 0;
var neg = false;
if (value.indexOf("-") == 0)
{
neg = true;
s = 1;
}
else if (value.indexOf("+") == 0)
{
s = 1;
}
if (s == 1 && value.length == 1)
return null;
var j = value.indexOf("0x");
if ((j == 0 || (j == 1 && s == 1)) && (radix == null || radix == 16))
{
s += 2;
if (radix == null) radix = 16;
}
else if (radix == null)
{
radix = 10;
}
var result = 0;
for (i in s...value.length)
{
var x = BASE.indexOf(value.charAt(i));
if (x == -1 || x >= radix)
{
if (i == s) return null;
else return neg ? -result : result;
}
result = (result * radix) + x;
}
return neg ? -result : result;
#end
}
}

View File

@@ -0,0 +1,103 @@
package yaml.util;
/// Wanted to use haxe.ds.ObjectMap under haxe3 but it doesn't support primitive keys on all targets so
/// we'll use our own TObjectMap instead.
//#if haxe3
//under haxe 3 use built in map for speed, even though this prevents us using a null key.
//typedef ObjectMap<K:{}, V> = haxe.ds.ObjectMap<K, V>;
//typedef AnyObjectMap = ObjectMap<{}, Dynamic>;
//#else
typedef ObjectMap<K, V> = TObjectMap<K, V>;
typedef AnyObjectMap = ObjectMap<Dynamic, Dynamic>;
/**
Cross platform object map which also supports the option of one null key.
*/
class TObjectMap<K, V>
{
var _keys:Array<K>;
var values:Array<V>;
public function new(?weakKeys:Bool = false)
{
_keys = [];
values = [];
}
public function set(key:K, value:V):Void
{
for (i in 0..._keys.length)
{
if (_keys[i] == key)
{
_keys[i] = key;
values[i] = value;
return;
}
}
_keys.push(key);
values.push(value);
}
public function get(key:K):Null<V>
{
for (i in 0..._keys.length)
{
if (_keys[i] == key)
return values[i];
}
return null;
}
public function exists(key:K):Bool
{
for (k in _keys)
if (k == key)
return true;
return false;
}
public function remove(key:K):Bool
{
for (i in 0..._keys.length)
{
if (_keys[i] == key)
{
_keys.splice(i, 1);
values.splice(i, 1);
return true;
}
}
return false;
}
public function keys():Iterator<K>
{
return _keys.iterator();
}
public function iterator():Iterator<V>
{
return values.iterator();
}
public function toString():String
{
var s = "{";
var ks:Dynamic = _keys;
var vs:Dynamic = values;
for (i in 0..._keys.length)
{
var k = (Std.is(ks[i], Array)) ? "[" + ks[i] + "]" : ks[i];
var v = (Std.is(vs[i], Array)) ? "[" + vs[i] + "]" : vs[i];
s += k + " => " + v + ", ";
}
if (_keys.length > 0)
s = s.substr(0, s.length - 2);
return s + "}";
}
}
//#end

View File

@@ -0,0 +1,7 @@
package yaml.util;
#if haxe3
typedef StringMap<T> = haxe.ds.StringMap<T>;
#else
typedef StringMap<T> = Hash<T>;
#end

View File

@@ -0,0 +1,40 @@
/*
Copyright (c) 2012 Massive Interactive
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package yaml.util;
/**
Utility methods for working with strings.
*/
class Strings
{
/**
Concatenate a source string with itself N number of times.
*/
public static function repeat(source:String, times:Int):String
{
var result = "";
for (i in 0...times)
result += source;
return result;
}
}

View File

@@ -0,0 +1,27 @@
package yaml.util;
class Utf8
{
public static function substring(value:String, startIndex:Int, ?endIndex:Null<Int>):String
{
var size = haxe.Utf8.length(value);
var pos = startIndex;
var length = 0;
if (endIndex == null)
{
length = size - pos;
}
else
{
if (startIndex > endIndex)
{
pos = endIndex;
endIndex = startIndex;
}
length = endIndex - pos;
}
return haxe.Utf8.sub(value, pos, length);
}
}