Sequence Update

This commit is contained in:
Josef 2024-05-12 19:03:20 +02:00
parent a8e7988c2a
commit a383c5da49
49 changed files with 6310 additions and 10 deletions

View File

@ -23,9 +23,9 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="false"
inkscape:zoom="10.363534"
inkscape:cx="-6.368484"
inkscape:cy="-0.048246091"
inkscape:zoom="20.727068"
inkscape:cx="4.1491638"
inkscape:cy="6.8750679"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

157
Icons/RJActionSequence.svg Normal file
View File

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg4"
sodipodi:docname="RJActionSequence.svg"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="false"
inkscape:zoom="20.727068"
inkscape:cx="4.1491638"
inkscape:cy="6.8750679"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g560" />
<g
id="g560"
transform="translate(0.08802232,0.240022)">
<g
id="g24518"
transform="matrix(1.0193268,0,0,1.0193268,0.13313592,0.48032339)">
<g
id="g24489"
transform="matrix(0.5277136,0,0,0.5277136,-2.2543173,-4.6044791)">
<path
sodipodi:type="star"
style="fill:#f7b200;fill-opacity:1;stroke:none;stroke-width:2.56963;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path24485"
inkscape:flatsided="false"
sodipodi:sides="5"
sodipodi:cx="-8.2018356"
sodipodi:cy="-2.1710742"
sodipodi:r1="7.9176526"
sodipodi:r2="4.5684857"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.94247777"
inkscape:rounded="0"
inkscape:randomized="0"
d="m -8.2018354,-10.088727 2.6852884,4.2216703 4.84484654,1.2492935 -3.18524704,3.85842889 0.3089911,4.99377581 -4.6538794,-1.8370299 -4.6538792,1.8370296 0.308991,-4.99377573 -3.185247,-3.85842907 4.844847,-1.2492932 z"
inkscape:transform-center-y="0.31212486"
transform="matrix(-0.41282678,0,0,-0.41282678,15.438508,17.099887)" />
<path
sodipodi:type="star"
style="fill:none;fill-opacity:1;stroke:#f7b200;stroke-width:0.56037;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill"
id="path24487"
inkscape:flatsided="false"
sodipodi:sides="5"
sodipodi:cx="-8.2018356"
sodipodi:cy="-2.1710742"
sodipodi:r1="7.9176526"
sodipodi:r2="4.7268386"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.9424778"
inkscape:rounded="0"
inkscape:randomized="0"
d="m -8.2018354,-10.088727 2.7783658,4.0935601 4.75176914,1.3774037 -3.03464454,3.90736248 0.1583886,4.94484222 -4.6538792,-1.6786771 -4.6538794,1.6786768 0.158389,-4.94484188 -3.034645,-3.90736292 4.751769,-1.3774033 z"
inkscape:transform-center-y="-0.6746144"
transform="matrix(0.89226704,0,0,0.89226704,26.142673,19.933344)" />
</g>
<g
id="g24501"
transform="matrix(0.5277136,0,0,0.5277136,-6.1140045,0.6060986)">
<path
sodipodi:type="star"
style="fill:#f7b200;fill-opacity:1;stroke:none;stroke-width:2.56963;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path24497"
inkscape:flatsided="false"
sodipodi:sides="5"
sodipodi:cx="-8.2018356"
sodipodi:cy="-2.1710742"
sodipodi:r1="7.9176526"
sodipodi:r2="4.5684857"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.94247777"
inkscape:rounded="0"
inkscape:randomized="0"
d="m -8.2018354,-10.088727 2.6852884,4.2216703 4.84484654,1.2492935 -3.18524704,3.85842889 0.3089911,4.99377581 -4.6538794,-1.8370299 -4.6538792,1.8370296 0.308991,-4.99377573 -3.185247,-3.85842907 4.844847,-1.2492932 z"
inkscape:transform-center-y="0.31212486"
transform="matrix(-0.41282678,0,0,-0.41282678,15.438508,17.099887)" />
<path
sodipodi:type="star"
style="fill:none;fill-opacity:1;stroke:#f7b200;stroke-width:0.56037;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill"
id="path24499"
inkscape:flatsided="false"
sodipodi:sides="5"
sodipodi:cx="-8.2018356"
sodipodi:cy="-2.1710742"
sodipodi:r1="7.9176526"
sodipodi:r2="4.7268386"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.9424778"
inkscape:rounded="0"
inkscape:randomized="0"
d="m -8.2018354,-10.088727 2.7783658,4.0935601 4.75176914,1.3774037 -3.03464454,3.90736248 0.1583886,4.94484222 -4.6538792,-1.6786771 -4.6538794,1.6786768 0.158389,-4.94484188 -3.034645,-3.90736292 4.751769,-1.3774033 z"
inkscape:transform-center-y="-0.6746144"
transform="matrix(0.89226704,0,0,0.89226704,26.142673,19.933344)" />
</g>
<g
id="g24507"
transform="matrix(0.5277136,0,0,0.5277136,1.5088777,0.58197555)">
<path
sodipodi:type="star"
style="fill:#f7b200;fill-opacity:1;stroke:none;stroke-width:2.56963;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path24503"
inkscape:flatsided="false"
sodipodi:sides="5"
sodipodi:cx="-8.2018356"
sodipodi:cy="-2.1710742"
sodipodi:r1="7.9176526"
sodipodi:r2="4.5684857"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.94247777"
inkscape:rounded="0"
inkscape:randomized="0"
d="m -8.2018354,-10.088727 2.6852884,4.2216703 4.84484654,1.2492935 -3.18524704,3.85842889 0.3089911,4.99377581 -4.6538794,-1.8370299 -4.6538792,1.8370296 0.308991,-4.99377573 -3.185247,-3.85842907 4.844847,-1.2492932 z"
inkscape:transform-center-y="0.31212486"
transform="matrix(-0.41282678,0,0,-0.41282678,15.438508,17.099887)" />
<path
sodipodi:type="star"
style="fill:none;fill-opacity:1;stroke:#f7b200;stroke-width:0.56037;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill"
id="path24505"
inkscape:flatsided="false"
sodipodi:sides="5"
sodipodi:cx="-8.2018356"
sodipodi:cy="-2.1710742"
sodipodi:r1="7.9176526"
sodipodi:r2="4.7268386"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.9424778"
inkscape:rounded="0"
inkscape:randomized="0"
d="m -8.2018354,-10.088727 2.7783658,4.0935601 4.75176914,1.3774037 -3.03464454,3.90736248 0.1583886,4.94484222 -4.6538792,-1.6786771 -4.6538794,1.6786768 0.158389,-4.94484188 -3.034645,-3.90736292 4.751769,-1.3774033 z"
inkscape:transform-center-y="-0.6746144"
transform="matrix(0.89226704,0,0,0.89226704,26.142673,19.933344)" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c6lekbldg584j"
path="res://.godot/imported/RJActionSequence.svg-861cbfffa33440d8d759203c534bd591.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Scripts/Rokojori/Rokojori-Action-Library/Icons/RJActionSequence.svg"
dest_files=["res://.godot/imported/RJActionSequence.svg-861cbfffa33440d8d759203c534bd591.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@ -4,12 +4,31 @@ using Godot;
namespace Rokojori
{
/** <summary for="class ActionList">
<title>
Executes multiple actions (RJAction) at once.
</title>
<description>
The ActionList, which is an RJAction itself, executes all actions stored in the member 'actions' and also child nodes
that extend RJAction, when 'triggerDirectChildren' is checked.
</description>
</summary>
*/
[GlobalClass, Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/RJActionList.svg") ]
public partial class ActionList : RJAction
{
{
/** <summary for="field actions">Actions to execute</summary>*/
[Export]
public RJAction[] actions;
[Export]
/** <summary for="field triggerDirectChildren">Whether to execute RJAction child nodes</summary>*/
[Export]
public bool triggerDirectChildren = true;
public override void _OnTrigger()
@ -27,6 +46,9 @@ namespace Rokojori
return;
}
Nodes.ForEachDirectChild<RJAction>( this, a => Actions.Trigger( a ) );
/*
var childCount = GetChildCount();
for ( int i = 0; i < childCount; i++ )
@ -41,6 +63,7 @@ namespace Rokojori
Actions.Trigger( action );
}
*/
}
}
}

View File

@ -0,0 +1,237 @@
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
namespace Rokojori
{
public class ActionSequenceRunner
{
public ActionSequence sequence;
public List<RJAction> actions;
public List<RJSequenceAction> sequencables;
public int sequencablesIndex = 0;
bool cancelled = false;
bool cancelledSequence = false;
int _runID = -1;
RJSequenceAction _runningAction;
int _runningActionID = -1;
public void Cancel()
{
if ( cancelled || _runningAction == null )
{
return;
}
cancelled = true;
_runningAction.CancelAction( _runningActionID );
}
void CancelRun()
{
if ( cancelledSequence )
{
return;
}
if ( ! cancelled )
{
cancelled = true;
}
cancelledSequence = true;
sequence.DispatchCancelled( _runID );
sequence.ClearRun( this );
}
public void ProcessNext()
{
RJLog.Log( "@" + sequence.Name, "Index:", sequencablesIndex );
if ( sequencablesIndex == 0 )
{
_runID = sequence.DispatchStart();
if ( sequencables.Count == 0 )
{
actions.ForEach( a => Actions.Trigger( a ) );
sequence.DispatchEnd( _runID );
sequence.ClearRun( this );
return;
}
}
if ( sequencablesIndex >= sequencables.Count )
{
TriggerAllAfter( sequencables[ sequencables.Count -1 ] );
sequence.DispatchEnd( _runID );
sequence.ClearRun( this );
return;
}
if ( cancelled )
{
CancelRun();
return;
}
var sequenceAction = sequencables[ sequencablesIndex ];
StartAction( sequenceAction );
}
Dictionary<RJSequenceAction,RJSequenceAction.OnSequenceDoneEventHandler> callbacks =
new Dictionary<RJSequenceAction, RJSequenceAction.OnSequenceDoneEventHandler>();
void StartAction( RJSequenceAction action )
{
var capturedAction = action;
RJSequenceAction.OnSequenceDoneEventHandler callback =
( long id, bool success ) =>
{
//RJLog.Log( "On Done", id, success );
if ( id != _runningActionID )
{
// RJLog.Error( "Invalid ID", id, "!=", _runningActionID );
return;
}
_runningActionID = -1;
if ( success )
{
sequencablesIndex ++;
ProcessNext();
}
else
{
sequence.DispatchCancelled( _runID );
sequence.ClearRun( this );
}
var callbackReference = callbacks[ capturedAction ];
capturedAction.OnSequenceDone -= callbackReference;
callbacks.Remove( capturedAction );
};
RunNext( action, callback );
/*
_runningAction = action;
callbacks[ _runningAction ] = callback;
_runningAction.OnSequenceDone += callback;
TriggerAllBefore( _runningAction );
Actions.Trigger( _runningAction );
_runningActionID = _runningAction.GetLastSequenceActionID();
*/
}
void RunNext( RJSequenceAction action, RJSequenceAction.OnSequenceDoneEventHandler callback )
{
_runningAction = action;
callbacks[ _runningAction ] = callback;
_runningAction.OnSequenceDone += callback;
TriggerAllBefore( _runningAction );
Actions.Trigger( _runningAction );
_runningActionID = _runningAction.GetLastSequenceActionID();
}
void TriggerAllBefore( RJSequenceAction action )
{
var actionIndex = actions.IndexOf( action );
for ( int i = actionIndex - 1; i >= 0; i -- )
{
if ( typeof( RJSequenceAction ).IsAssignableFrom( actions[ i ].GetType() ) )
{
return;
}
RJLog.Log( "Triggering Action", actions[ i ].Name );
Actions.Trigger( actions[ i ] );
}
}
void TriggerAllAfter( RJSequenceAction action )
{
var actionIndex = actions.IndexOf( action );
for ( int i = actionIndex + 1; i < actions.Count; i ++ )
{
if ( typeof( RJSequenceAction ).IsAssignableFrom( actions[ i ].GetType() ) )
{
return;
}
RJLog.Log( "Triggering Action", actions[ i ].Name );
Actions.Trigger( actions[ i ] );
}
}
}
[GlobalClass, Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/RJActionSequence.svg") ]
public partial class ActionSequence:RJSequenceAction
{
/** <summary for="field actions">Actions to execute</summary>*/
[Export]
public RJAction[] actions;
[Export]
public bool triggerDirectChildren = true;
List<ActionSequenceRunner> running = new List<ActionSequenceRunner>();
public override void _OnTrigger()
{
var run = new ActionSequenceRunner();
run.sequence = this;
run.actions = new List<RJAction>( actions );
if ( triggerDirectChildren )
{
Nodes.ForEachDirectChild<RJAction>( this, a => run.actions.Add( a ) );
}
run.sequencables = Lists.FilterType<RJAction,RJSequenceAction>( run.actions );
// RJLog.Log( "Running", HierarchyName.Of( this ), run.sequencables.Count );
running.Add( run );
run.ProcessNext();
}
public override void CancelAction( int id )
{
running.ForEach( r => r.Cancel() );
}
public void ClearRun( ActionSequenceRunner run )
{
running.Remove( run );
}
}
}

View File

@ -11,7 +11,7 @@ namespace Rokojori
if ( action == null )
{
return;
}
}
action.Trigger();
}

57
Runtime/Actions/Delay.cs Normal file
View File

@ -0,0 +1,57 @@
using Godot;
namespace Rokojori
{
[GlobalClass]
public partial class Delay : RJSequenceAction
{
[Export]
public float duration;
[Export]
public string message;
float elapsed = 0;
bool running = false;
bool finished = false;
int runID = 0;
public override void _Ready()
{
/// OnSequenceDone += ( id, success ) => { RJLog.Log( "OnSequenceDone", Name ); } ;
}
public override void _OnTrigger()
{
//RJLog.Log( "Starting", Name );
runID = DispatchStart();
elapsed = 0;
running = true;
finished = false;
}
public override void _Process( double delta )
{
if ( ! running )
{
return;
}
elapsed += (float) delta;
if ( elapsed > duration )
{
running = false;
finished = true;
RJLog.Log( Name, " >> '" + message + "'" );
DispatchEnd( runID );
}
}
}
}

View File

@ -0,0 +1,34 @@
class_name GDDelay
extends RJSequenceAction
@export var duration = 1
@export var message = "huhu"
var running = false
var elapsed = 0;
var id = 0;
var cachedIDs = []
func _onTrigger():
if running:
cachedIDs
running = true
elapsed = 0
id = dispatchStart()
func _process( delta ):
if ! running:
return
elapsed += delta
if elapsed > duration:
running = false
elapsed = 0
print( name + " >> " + message )
dispatchEnd( id )

View File

@ -0,0 +1,80 @@
using Godot;
using System.Collections.Generic;
namespace Rokojori
{
[GlobalClass]
public partial class LoadScene : RJSequenceAction
{
[Export]
public string scenePath;
[Export]
public Node target;
[Export]
public RJAction onLoaded;
bool _loading = false;
int _id = -1;
List<int> _cached = new List<int>();
public override void _OnTrigger()
{
if ( _loading )
{
_cached.Add( DispatchStart() );
return;
}
_loading = true;
_id = DispatchStart();
var errorCode = ResourceLoader.LoadThreadedRequest( scenePath );
if ( Error.Ok != errorCode )
{
_cached.Add( _id );
_id = -1;
_loading = false;
}
}
public override void _Process ( double delta )
{
if ( ! _loading )
{
return;
}
var status = ResourceLoader.LoadThreadedGetStatus( scenePath );
if ( ResourceLoader.ThreadLoadStatus.Loaded != status )
{
return;
}
_loading = false;
var packedScene = (PackedScene) ResourceLoader.LoadThreadedGet( scenePath );
var node = packedScene.Instantiate();
target.AddChild( node );
Actions.Trigger( onLoaded );
DispatchEnd( _id );
_id = -1;
_cached.ForEach(
( c )=>
{
target.AddChild( packedScene.Instantiate() );
Actions.Trigger( onLoaded );
DispatchEnd( c );
}
);
_cached.Clear();
}
}
}

View File

@ -12,7 +12,7 @@ namespace Rokojori
public override void _Ready()
{
RJLog.Log( "OnReady" );
// RJLog.Log( "OnReady" );
Actions.Trigger( action );
}
}

204
Runtime/Colors/HSLColor.cs Normal file
View File

@ -0,0 +1,204 @@
using Godot;
namespace Rokojori
{
public struct HSLColor
{
public static readonly HSLColor white = new HSLColor( new Color( 1, 1, 1, 1 ) );
public static readonly HSLColor red = new HSLColor( new Color( 1, 0, 0, 1 ) );
public static readonly HSLColor green = new HSLColor( new Color( 0, 1, 0, 1 ) );
public static readonly HSLColor blue = new HSLColor( new Color( 0, 0, 1, 1 ) );
public static readonly HSLColor black = new HSLColor( new Color( 0, 0, 0, 1 ) );
// hue in degrees 0 - 360
public float h;
// saturation 0 - 1
public float s;
// luminance 0 - 1
public float l;
// alpha 0 - 1
public float a;
public HSLColor( float h, float s, float l, float a )
{
this.h = h;
this.s = s;
this.l = l;
this.a = a;
}
public HSLColor( float h, float s, float l )
{
this.h = h;
this.s = s;
this.l = l;
this.a = 1f;
}
public float lumaBT601
{
get
{
var rgb = ToRGBA();
var R = rgb.R;
var G = rgb.G;
var B = rgb.B;
return 0.2989f * R + 0.5870f * G + 0.1140f * B;
}
}
public float lumaBT709
{
get
{
var rgb = ToRGBA();
var R = rgb.R;
var G = rgb.G;
var B = rgb.B;
return 0.2126f * R + 0.7152f * G + 0.0722f * B;
}
}
public float normalizedHue
{
get { return h / 360f;}
}
public HSLColor( Color c )
{
HSLColor temp = FromRGBA( c );
h = temp.h;
s = temp.s;
l = temp.l;
a = temp.a;
}
public static HSLColor FromRGBA( Color c )
{
float h, s, l, a;
a = c.A;
float cmin = Mathf.Min( Mathf.Min( c.R, c.G ), c.B );
float cmax = Mathf.Max( Mathf.Max( c.R, c.G ), c.B );
l = ( cmin + cmax ) / 2f;
if ( cmin == cmax )
{
s = 0;
h = 0;
}
else
{
float delta = cmax - cmin;
s = ( l <= .5f ) ? ( delta / ( cmax + cmin ) ) : ( delta / ( 2f - ( cmax + cmin ) ) );
h = 0;
if ( c.R == cmax )
{
h = ( c.G - c.B ) / delta;
}
else if ( c.G == cmax )
{
h = 2f + ( c.B - c.R ) / delta;
}
else if ( c.B == cmax )
{
h = 4f + ( c.R - c.G ) / delta;
}
h = MathX.Repeat( h * 60f, 360f );
}
return new HSLColor( h, s, l, a );
}
public static HSLColor Lerp( HSLColor x, HSLColor y, float t )
{
float h = Mathf.LerpAngle( x.h, y.h, t );
float s = Mathf.Lerp( x.s, y.s, t );
float l = Mathf.Lerp( x.l, y.l, t );
float a = Mathf.Lerp( x.a, y.a, t );
return new HSLColor( h, s, l, a );
}
public static Color Lerp( Color x, Color y, float t )
{
var hx = new HSLColor( x );
var hy = new HSLColor( y );
var h = Lerp( hx, hy, t );
return h.ToRGBA();
}
public Color ToRGBA( )
{
float r, g, b, a;
a = this.a;
float m1, m2;
m2 = ( l <= .5f ) ? ( l * ( 1f + s ) ) : ( l + s - l * s );
m1 = 2f * l - m2;
if ( s == 0f )
{
r = g = b = l;
}
else
{
r = Value( m1, m2, h + 120f );
g = Value( m1, m2, h );
b = Value( m1, m2, h - 120f );
}
return new Color( r, g, b, a );
}
static float Value( float n1, float n2, float hue )
{
hue = MathX.Repeat( hue, 360f );
if ( hue < 60f )
{
return n1 + ( n2 - n1 ) * hue / 60f;
}
else if ( hue < 180f )
{
return n2;
}
else if ( hue < 240f )
{
return n1 + ( n2 - n1 ) * ( 240f - hue ) / 60f;
}
else
{
return n1;
}
}
public static implicit operator HSLColor( Color src )
{
return FromRGBA( src );
}
public static implicit operator Color( HSLColor src )
{
return src.ToRGBA( );
}
}
}

169
Runtime/Files/FilePath.cs Normal file
View File

@ -0,0 +1,169 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace Rokojori
{
public enum FilePathType
{
ABSOLUTE,
RELATIVE
}
public class FilePath
{
public FilePathType type;
public FilePath parent;
public string path;
public string _fileName;
public string _fileExtension;
string _fullPath = null;
/**<summary for="method get_fileName">Only fileName without fileExtension </summary>*/
public string fileName
{
get
{
if ( _fileName == null)
{
_fileName = Path.GetFileNameWithoutExtension( path );
}
return _fileName;
}
}
/**<summary for="method get_fullFileName">Combines fileName + fileExtension </summary>*/
public string fullFileName => fileName + fileExtension;
/**<summary for="method get_fileExtension>File extension including the dot, eg. ".svg", ".jpg", ".js"</summary>*/
public string fileExtension
{
get
{
if ( _fileExtension == null)
{
_fileExtension = Path.GetExtension( path );
}
return _fileExtension;
}
}
public string fullPath
{
get
{
if ( _fullPath == null )
{
if ( this.parent != null )
{
_fullPath = this.parent.fullPath + "/" + path;
}
else
{
_fullPath = path;
}
}
return _fullPath;
}
}
public static FilePath Create( string path, FilePathType type = FilePathType.RELATIVE, FilePath parent = null )
{
var rp = new FilePath();
rp.type = type;
rp.parent = parent;
rp.path = path;
return rp;
}
public static FilePath Absolute( string path )
{
return FilePath.Create( path, FilePathType.ABSOLUTE );
}
public FilePath MakeRelative( string path )
{
return FilePath.Create( path, FilePathType.RELATIVE, this );
}
public FilePath MakeAbsolutePathRelative( string absolutePath )
{
absolutePath = FilePath.Normalize( absolutePath );
var ownAbsolutePath = FilePath.Normalize( fullPath );
if ( ! absolutePath.StartsWith( ownAbsolutePath ) )
{
return null;
}
var relativePath = absolutePath.Substring( ownAbsolutePath.Length );
return MakeRelative( relativePath );
}
public FilePath CreateRelativeFromAbsolutePathWithParent( string absolutePath, FilePath absoluteParent )
{
var rp = new FilePath();
rp.type = FilePathType.RELATIVE;
rp.parent = absoluteParent;
rp.path = Path.GetFileName( absolutePath );
rp._fullPath = absolutePath;
return rp;
}
public static string Normalize( string path, bool removeFirst = true, bool removeLast = true )
{
path = Regex.Replace( path, @"\\", "/" );
path = Regex.Replace( path, @"/+", "/" );
if ( removeFirst )
{
path = Regex.Replace( path, @"^/", "" );
}
if ( removeLast )
{
path = Regex.Replace( path, @"/$", "" );
}
return path;
}
public static string Join( params string[] paths )
{
var sb = new StringBuilder();
for ( int i = 0; i < paths.Length; i++ )
{
var path = paths[ i ];
path = Normalize( path );
if ( i != 0 )
{
sb.Append( "/" );
}
sb.Append( path );
}
return sb.ToString();
}
}
}

138
Runtime/Files/FilesSync.cs Normal file
View File

@ -0,0 +1,138 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace Rokojori
{
public class FilesSync
{
public static bool DirectoryExists( string path )
{
return Directory.Exists( path );
}
public static void CreateDirectory( string path )
{
Directory.CreateDirectory( path );
}
public static void EnsureDirectoryExists( string path )
{
if ( DirectoryExists( path ) )
{
return;
}
CreateDirectory( path );
}
public static List<FilePath> GetDirectories( string path, System.Func<FilePath,bool> evaluator = null )
{
var list = new List<FilePath>();
var files = Directory.GetDirectories( path, "*", SearchOption.TopDirectoryOnly );
var absolutePath = FilePath.Absolute( path );
for ( int i = 0; i < files.Length; i++ )
{
var fileName = Path.GetFileName( files[ i ] );
var relativePath = absolutePath.MakeRelative( fileName );
if ( evaluator == null || evaluator( relativePath ) )
{
list.Add( relativePath );
}
}
return list;
}
public static List<FilePath> GetFiles( string path, System.Func<FilePath,bool> evaluator = null, bool recursive = false )
{
var list = new List<FilePath>();
var files = Directory.GetFiles( path, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly );
var absoluteFilePath = FilePath.Absolute( path );
for ( int i = 0; i < files.Length; i++ )
{
var relativePath = absoluteFilePath.MakeAbsolutePathRelative( files[ i ] );
if ( evaluator == null || evaluator( relativePath ) )
{
list.Add( relativePath );
}
}
return list;
}
public static bool SaveUTF8( string path, string data )
{
try
{
using ( StreamWriter streamWriter = File.CreateText( path ) )
{
streamWriter.Write( data );
}
}
catch ( System.Exception e )
{
RJLog.Log( "Could not save text: ", path, "exception", e );
return false;
}
return true;
}
public static bool SaveJSON( string path, object obj )
{
return SaveUTF8( path, JSON.StringifyObject( obj ) );
}
public static string LoadUTF8( string path )
{
try
{
using ( StreamReader streamReader = File.OpenText( path ) )
{
return streamReader.ReadToEnd();
}
}
catch ( System.Exception e )
{
RJLog.Log( "Could not load text: ", path, "exception", e );
}
return null;
}
public static bool SaveBytes( string fileName, byte[] byteArray )
{
try
{
using ( var fs = new FileStream( fileName, FileMode.Create, FileAccess.Write ) )
{
fs.Write( byteArray, 0, byteArray.Length );
return true;
}
}
catch ( System.Exception e )
{
RJLog.Log( "Exception caught in process: {0}", e );
return false;
}
}
}
}

View File

@ -37,13 +37,35 @@ namespace Rokojori
if ( node is T )
{
return (T)node;
return (T) node;
}
}
return null;
}
public static void ForEachDirectChild<T>( Node parent, System.Action<T> action ) where T:Node
{
if ( parent == null || action == null )
{
return;
}
var numChildren = parent.GetChildCount();
for ( int i = 0; i < numChildren; i++ )
{
var node = parent.GetChild( i );
if ( ! ( node is T ) )
{
continue;
}
action( node as T );
}
}
static NodesWalker nodesWalker = new NodesWalker();
public static T GetAnyChild<T>( Node parent ) where T:Node
@ -63,6 +85,8 @@ namespace Rokojori
SetState.SetStateOfNode( NodeStateType.Disabled, n, affectProcess, affectPhysicsProcess, affectInput );
}
*/
public static void Iterate( Node[] nodes, System.Action<Node> callback )
{
for ( int i = 0; i < nodes.Length; i++ )
@ -71,7 +95,7 @@ namespace Rokojori
}
}
*/
}
}

View File

@ -12,12 +12,64 @@ namespace Rokojori
static void Stringify( object obj, StringBuilder output )
{
if ( obj == null )
{
output.Append( "<null>" );
return;
}
output.Append( obj.ToString() );
}
static void LogMessage( string message )
{
GD.Print( message );
var trace = GetTrace();
GD.PrintRich("[b]" + message + "[/b]");
GD.PrintRich( trace );
}
static void LogErrorMessage( string message )
{
var trace = GetTrace();
GD.PrintErr( message );
GD.PrintRich( trace );
}
static string GetTrace()
{
var stackTrace = new System.Diagnostics.StackTrace( true );
var frameIndex = 3;
var frame = stackTrace.GetFrame( frameIndex );
var className = frame.GetFileName();
if ( className != null )
{
var slashIndex = -1;
for ( int i = className.Length - 1; i >= 0 && slashIndex == -1; i-- )
{
if ( className[ i ] == '\\' || className[ i ] == '/' )
{
slashIndex = i;
}
}
if ( slashIndex != -1 )
{
var end = className.EndsWith( ".cs" ) ? className.Length - 3 : className.Length;
className = className.Substring( slashIndex + 1, end - ( slashIndex + 1 ) );
}
}
if ( className == null )
{
className ="<Unknown>";
}
var trace = className + "." + frame.GetMethod().Name + "() ln."+ frame.GetFileLineNumber();
return "[color=gray] " + trace + "[/color]" ;
}
public static void Log( params object[] objects)
@ -36,6 +88,23 @@ namespace Rokojori
LogMessage( sb.ToString() );
}
public static void Error( params object[] objects)
{
var sb = new StringBuilder();
for ( int i = 0; i < objects.Length; i++ )
{
if ( i != 0 )
{
sb.Append( " " );
}
Stringify( objects[ i ], sb );
}
LogErrorMessage( sb.ToString() );
}
}
}

228
Runtime/Math/MathX.cs Normal file
View File

@ -0,0 +1,228 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System.Text;
using System;
namespace Rokojori
{
public class MathX
{
public static float Clamp01( float value )
{
return Mathf.Clamp( value, 0, 1 );
}
public static float Normalize( float value, float min, float max )
{
return ( value - min ) / ( max - min );
}
public static float NormalizeClamped( float value, float min, float max )
{
return MathX.Clamp01( Normalize( value, min, max ) );
}
public static float Map( float value, float inputMin, float inputMax,
float outputMin, float outputMax )
{
var normalized = Normalize( value, inputMin, inputMax );
return normalized * ( outputMax - outputMin ) + outputMin;
}
public static float MapPositive( float value, float inputMax, float outputMax )
{
return Map( value, 0, inputMax, 0, outputMax );
}
public static float MapClamped( float value, float inputMin, float inputMax,
float outputMin, float outputMax )
{
var normalized = NormalizeClamped( value, inputMin, inputMax );
return normalized * ( outputMax - outputMin ) + outputMin;
}
public static float Map01( float value, float outputMin, float outputMax )
{
return value * ( outputMax - outputMin ) + outputMin;
}
public static float MapPolar( float value, float min, float max )
{
return Map01( value * 0.5f + 0.5f, min, max );
}
public static float MapPolarTo01( float value)
{
return value * 0.5f + 0.5f;
}
public static float Repeat( float value, float length )
{
while ( value > length )
{
value -=length;
}
while ( value < 0 )
{
value += length;
}
return value;
}
public static float Triangle( float value )
{
value = MathX.Repeat( value, 1 ) * 2;
if ( value > 1 )
{
value = 2 - value;
}
return value;
}
public static float EaseSine( float value )
{
return Mathf.Sin( Mathf.Pi * ( value * 0.5f + 1.5f ) ) + 1;
}
public static int Sign( int value )
{
return value == 0 ? 0 : value < 0 ? -1 : 1;
}
public static T LerpList<T>( List<T> data, float t, Func<T,T,float,T> lerpElementsFunction,
int dataFillSize = -1 )
{
dataFillSize = dataFillSize == -1 ? data.Count : dataFillSize;
var floatIndex = t * dataFillSize;
if ( floatIndex <= 0 )
{
return data[ 0 ];
}
if ( floatIndex >= ( dataFillSize - 1 ) )
{
return data[ dataFillSize - 1 ];
}
var flooredIndex = Mathf.FloorToInt( floatIndex );
var ceiledIndex = flooredIndex + 1;
var flooredValue = data[ flooredIndex ];
var ceiledValue = data[ ceiledIndex ];
var interpolationAmount = floatIndex - flooredIndex;
return lerpElementsFunction( flooredValue, ceiledValue, interpolationAmount );
}
public static float PolarTriangle( float value )
{
return Triangle( value ) * 2f - 1f;
}
public static float Step( float phase, float phaseStart, float phaseEnd )
{
if ( phase < phaseStart )
{
return 0;
}
if ( phase >= phaseEnd )
{
return 1;
}
return ( phase - phaseStart ) / ( phaseStart - phaseEnd );
}
public static int Repeat( int value, int range )
{
while ( value < 0 )
{
value += range;
}
while ( value >= range )
{
value -= range;
}
return value;
}
public static float PolarRepeat( float value, float range )
{
while ( value < -range )
{
value += range * 2;
}
while ( value >= range )
{
value -= range * 2;
}
return value;
}
public static float Exponent( float base_, float power )
{
return Mathf.Log( power ) / Mathf.Log( base_ );
}
public static float Base( float exponent, float power )
{
return Mathf.Pow( power, 1f / exponent );
}
public static float SmoothingCoefficient( float ms, float reachingTarget = 0.1f, float frameDurationMS = 16.66666666f )
{
return 1f - Base( ms / frameDurationMS, reachingTarget );
}
public static float SmoothValue( float oldValue, float newValue, float ms, float reachingTarget = 0.1f, float frameDurationMS = 16.66666666f )
{
return oldValue + SmoothingCoefficient( ms, reachingTarget, frameDurationMS ) * ( newValue - oldValue );
}
public static float SmoothDegrees( float oldValue, float newValue, float ms, float reachingTarget = 0.1f, float frameDurationMS = 16.66666666f )
{
oldValue = Mathf.Wrap( oldValue, 0, 360 );
newValue = Mathf.Wrap( newValue, 0, 360 );
var difference = newValue - oldValue;
if ( Mathf.Abs( difference ) > 180 )
{
if ( newValue > oldValue )
{
oldValue += 360;
}
else
{
newValue += 360;
}
}
return oldValue + SmoothingCoefficient( ms, reachingTarget, frameDurationMS ) * ( newValue - oldValue );
}
public static Vector3 SmoothVector3( Vector3 oldValue, Vector3 newValue, float ms, float reachingTarget = 0.1f, float frameDurationMS = 16.66666666f )
{
return oldValue + SmoothingCoefficient( ms, reachingTarget, frameDurationMS ) * ( newValue - oldValue );
}
}
}

View File

85
Runtime/Random/LCG.cs Normal file
View File

@ -0,0 +1,85 @@
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
namespace Rokojori
{
[System.Serializable]
public class LCGSeedState
{
public int modulus;
public int multiplier;
public int increment;
public int state;
}
public class LCG:SeedableRandomEngine
{
private LCGSeedState _seedState = new LCGSeedState();
private float _normalizer;
public LCG()
{
this.setParameters( (int)Mathf.Pow(2,32), 1664525, 1013904223 );
}
public static LCG Randomized()
{
var ms = Time.GetUnixTimeFromSystem() % 1;
var seed = (int) ( ms * 10000 );
var lcg = new LCG();
RJLog.Log( "Seed", seed, Time.GetUnixTimeFromSystem() );
lcg.SetSeed( seed );
lcg.Next();
return lcg;
}
void setParameters( int modulus, int multiplier, int increment )
{
_seedState.modulus = modulus;
_seedState.multiplier = multiplier;
_seedState.increment = increment;
_seedState.state = 0;
this._normalizer = 1f/(_seedState.modulus);
}
public override object GetSeedState()
{
var seedState = new LCGSeedState();
seedState.modulus = _seedState.modulus;
seedState.multiplier = _seedState.multiplier;
seedState.increment = _seedState.increment;
seedState.state = _seedState.state;
return seedState;
}
public override void SetSeedState( object seedStateObject )
{
var seedState = (LCGSeedState) seedStateObject;
_seedState.modulus = seedState.modulus;
_seedState.multiplier = seedState.multiplier;
_seedState.increment = seedState.increment;
_seedState.state = seedState.state;
this._normalizer = 1 / ( _seedState.modulus );
}
public override float Next()
{
_seedState.state = ( _seedState.state * _seedState.multiplier + _seedState.increment) % _seedState.modulus;
var value = _seedState.state * this._normalizer;
return Mathf.Abs( value );
}
public override void SetSeed( int seed )
{
_seedState.state = seed % _seedState.modulus;
}
}
}

View File

@ -0,0 +1,219 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public abstract class RandomEngine
{
public abstract float Next();
public float Value( float scalar )
{
return Next() * scalar;
}
public float Range( float a, float b)
{
return this.Next()*( b - a ) + a;
}
public float Polar()
{
return this.Next() * 2f - 1f;
}
public float Polar( float value )
{
return Polar() * value;
}
public bool Bool()
{
return this.Next() > 0.5f;
}
public bool Chance( float value )
{
return value / 100f >= Next();
}
public bool FlipCoin()
{
return Chance( 50f );
}
public float PercentageVariation( float variation )
{
var value = ( 100f - variation ) / 100f;
return Range( value, 1f / value );
}
public bool WithChanceOf( float value )
{
return ( this.Next() * 100 ) <= value ;
}
public Vector3 Between( Vector3 a, Vector3 b )
{
return a.Lerp( b, this.Next() );
}
public Color RandomHue( float saturation = 1f, float luminance = 0.5f)
{
var hue = Range( 0, 360 );
var color = new HSLColor( hue, saturation, luminance );
return color;
}
public Vector2 InRectangle( Vector2 min, Vector2 max )
{
var x = Mathf.Lerp( min.X, max.X, Next() );
var y = Mathf.Lerp( min.Y, max.Y, Next() );
return new Vector2( x, y );
}
public Vector3 InCube()
{
return new Vector3( Polar(), Polar(), Polar() );
}
public Vector3 InsideCube( float size )
{
return InCube() * ( size * 0.5f );
}
public Vector3 InsideSphere()
{
var inCube = InCube();
if ( inCube.LengthSquared() > 1 )
{
inCube = inCube.Normalized() * Next();
}
return inCube;
}
public Vector3 OnSphere( float size )
{
return OnSphere() * size;
}
public Vector3 OnSphere()
{
var dir = InsideSphere();
while ( dir.LengthSquared() == 0 )
{
dir = InsideSphere();
}
return dir;
}
public Vector3 InSphere( float size )
{
return InsideSphere() * ( size * 0.5f );
}
public int IntegerInclusive( int min, int max )
{
return (int) ( Mathf.Floor( this.Next() * ( max - min + 1 ) ) + min ) ;
}
public int IntegerInclusive( int max )
{
return IntegerInclusive( 0, max );
}
public int IntegerExclusive( int min, int max )
{
var nextValue = this.Next();
var randomValue = nextValue * ( max - min ) + min;
var value = (int) ( Mathf.Floor( randomValue ) );
return Mathf.Min( max - 1, value );
}
public int IntegerExclusive( int max )
{
return IntegerExclusive( 0, max );
}
public char From( string source )
{
var index = IntegerExclusive( source.Length );
return source[ index ];
}
public T From<T>( T[] array )
{
if ( array.Length == 0 )
{
return default ( T );
}
var index = IntegerExclusive( array.Length );
return array[ index ];
}
public T From<T>( HashSet<T> set )
{
var selectedIndex = IntegerExclusive( set.Count );
var currentIndex = 0;
foreach ( var e in set )
{
if ( selectedIndex == currentIndex )
{
return e;
}
currentIndex++;
}
return default( T );
}
public T FromValues<T>( T first, params T[] values )
{
if ( values.Length == 0 )
{
return first;
}
var index = IntegerExclusive( values.Length + 1 );
return index == 0 ? first : values[ index - 1 ];
}
public T From<T>( List<T> list )
{
if ( list.Count == 0 )
{
return default ( T );
}
var index = this.IntegerExclusive( 0, list.Count );
return list[ index ];
}
}
public abstract class SeedableRandomEngine:RandomEngine
{
public abstract void SetSeed( int number );
public abstract object GetSeedState();
public abstract void SetSeedState( object seedState );
}
}

35
Runtime/Text/JSON/JSON.cs Normal file
View File

@ -0,0 +1,35 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Rokojori
{
public class JSON
{
public static string Stringify( JSONData jsonData )
{
return jsonData.Stringify();
}
public static JSONData Parse( string jsonString )
{
return new JSONParser().Parse( jsonString );
}
public static string StringifyObject( object value )
{
var serializer = new JSONSerializer( new JSONSerializationSettings() );
return serializer.Serialize( value );
}
public static T ParseObject<T>( string jsonString ) where T:new()
{
var deserializer = new JSONDeserializer( new JSONSerializationSettings() );
return deserializer.Deserialize<T>( jsonString );
}
}
}

View File

@ -0,0 +1,102 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Rokojori
{
public class JSONArray:JSONData
{
List<JSONData> _values = new List<JSONData>();
public override JSONDataType dataType
{
get { return JSONDataType.ARRAY; }
}
public List<JSONData> values { get { return _values; }}
public int size { get { return _values.Count; } }
public JSONData Get( int index )
{
return _values[ index ];
}
public void Delete( int index )
{
_values.RemoveAt( index );
}
public void Push( JSONData value ) {_values.Add( value );}
public void Push( string value ) { Push( new JSONValue( value ) ); }
public void Push( double value ) { Push( new JSONValue( value ) ); }
public void Push( bool value ) { Push( new JSONValue( value ) ); }
public void Set( int index, JSONData data )
{
while ( index >= _values.Count )
{
Push( new JSONValue( null ) );
}
_values[ index ] = data;
}
public void Set( int index, string value ) { Set( index, new JSONValue( value ) ); }
public void Set( int index, bool value ) { Set( index, new JSONValue( value ) ); }
public void Set( int index, double value ) { Set( index, new JSONValue( value ) ); }
public JSONData this[ int index ]
{
get { return Get( index ); }
set { Set( index, value ); }
}
public JSONData Pop()
{
return Lists.Pop( _values );
}
public override string Stringify( StringifyOptions options )
{
var builder = new StringBuilder();
if ( values.Count == 0)
{
options.increaseIndent();
builder.Append( "[]" );
options.decreaseIndent();
return builder.ToString();
}
options.increaseIndent();
builder.Append( "[\n" );
if ( values.Count > 0 )
{
builder.Append( options.indent );
builder.Append( values[ 0 ].Stringify( options ) );
}
for ( var i = 1; i < values.Count; i++ )
{
builder.Append( ",\n" );
builder.Append( options.indent );
builder.Append( values[ i ].Stringify( options ) );
}
options.decreaseIndent();
builder.Append( "\n" );
builder.Append( options.indent );
builder.Append( "]" );
return builder.ToString();
}
}
}

View File

@ -0,0 +1,232 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Rokojori
{
public enum JSONDataType
{
BOOLEAN,
NUMBER,
STRING,
OBJECT,
ARRAY,
NULL
}
public class StringifyOptions
{
public bool pretty = true;
public string indentString = " ";
int _indentLevel = -1;
string _indent = "";
public void increaseIndent(){ _indentLevel++; UpdateIndent(); }
public void decreaseIndent(){ _indentLevel--; UpdateIndent(); }
public string indent{ get { return _indent; }}
void UpdateIndent()
{
_indent = "";
for ( var i = 0; i < _indentLevel; i++ )
{
_indent += indentString;
}
}
}
public abstract class JSONData
{
public abstract JSONDataType dataType { get; }
public abstract string Stringify( StringifyOptions options );
public string Stringify()
{
return Stringify( new StringifyOptions() );
}
public bool isArray
{
get { return dataType == JSONDataType.ARRAY; }
}
public bool isObject
{
get { return dataType == JSONDataType.OBJECT; }
}
public bool isNull
{
get { return dataType == JSONDataType.NULL; }
}
public bool isNotNull
{
get { return dataType != JSONDataType.NULL; }
}
public bool isNumber
{
get { return dataType == JSONDataType.NUMBER; }
}
public bool isBoolean
{
get { return dataType == JSONDataType.BOOLEAN; }
}
public bool isString
{
get { return dataType == JSONDataType.STRING; }
}
public JSONArray AsArray()
{
if ( isArray )
{
return (JSONArray)this;
}
return null;
}
public JSONObject AsObject()
{
if ( isObject )
{
return (JSONObject)this;
}
return null;
}
public static int GetInt( JSONData data, int alternative = 0 )
{
if ( data == null || ! data.isNumber )
{
return alternative;
}
return data.intValue;
}
public static float GetFloat( JSONData data, float alternative = 0 )
{
if ( data == null || ! data.isNumber )
{
return alternative;
}
return data.floatValue;
}
public static double GetNumber( JSONData data, double alternative = 0 )
{
if ( data == null || ! data.isNumber )
{
return alternative;
}
return data.numberValue;
}
public static bool GetBool( JSONData data, bool alternative = false )
{
if ( data == null || ! data.isBoolean )
{
return alternative;
}
return data.booleanValue;
}
public static string GetString( JSONData data, string alternative = "" )
{
if ( data == null || ! data.isString )
{
return alternative;
}
return data.stringValue;
}
public double numberValue
{
get
{
var value = (JSONValue) this;
return value.GetNumberValue();
}
}
public float floatValue
{
get
{
var value = (JSONValue) this;
return value.GetFloatValue();
}
}
public int intValue
{
get
{
var value = (JSONValue) this;
return value.GetIntValue();
}
}
public bool booleanValue
{
get
{
var value = (JSONValue) this;
return value.GetBooleanValue();
}
}
public string stringValue
{
get
{
var value = (JSONValue) this;
return value.GetStringValue();
}
}
public object enumValue( System.Type enumType )
{
var value = intValue;
try
{
var enumValue = System.Enum.ToObject( enumType , intValue );
return enumValue;
}
catch ( System.Exception e )
{
RJLog.Log(
"Exception:", e, "\n",
"Couldn't parse enum value", value ,
"JSON Data Type", dataType,
"Stringified Value:", JSON.Stringify( this )
);
}
return null;
}
}
}

View File

@ -0,0 +1,340 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Rokojori
{
public enum JSONLexerEventType
{
DONE_SUCCESS,
DONE_ERROR,
OBJECT_START,
OBJECT_END,
ARRAY_START,
ARRAY_END,
IDENTIFIER,
STRING,
NUMBER,
TRUE,
FALSE,
NULL,
ARRAY_SEPERATOR,
IDENTIFIER_SEPERATOR,
ERROR_UNEXPECTED_SYMBOL,
ERROR_UNCLOSED_STRING,
ERROR_UNCLOSED_OBJECT,
ERROR_UNCLOSED_ARRAY,
ERROR_EXPECTED_FALSE,
ERROR_EXPECTED_TRUE,
ERROR_EXPECTED_NULL,
ERROR_INFINITY_PREVENTION
}
public class JSONLexer
{
public static bool IsErrorType( JSONLexerEventType type )
{
switch( type )
{
case JSONLexerEventType.ERROR_UNEXPECTED_SYMBOL:
case JSONLexerEventType.ERROR_UNCLOSED_STRING:
case JSONLexerEventType.ERROR_UNCLOSED_OBJECT:
case JSONLexerEventType.ERROR_UNCLOSED_ARRAY:
case JSONLexerEventType.ERROR_EXPECTED_FALSE:
case JSONLexerEventType.ERROR_EXPECTED_TRUE:
case JSONLexerEventType.ERROR_EXPECTED_NULL:
case JSONLexerEventType.ERROR_INFINITY_PREVENTION:
case JSONLexerEventType.DONE_ERROR:
{
return true;
}
}
return false;
}
public delegate void OnParse( JSONLexerEventType type, int offset, int length );
string source;
OnParse onParseCallback;
public void Lex( string source, OnParse onParseCallback = null )
{
this.source = source;
this.onParseCallback = onParseCallback;
Call( LexJSON(), 0 );
}
void Call( JSONLexerEventType type, int offset, int length = -1 )
{
if ( onParseCallback != null )
{
onParseCallback( type, offset, length );
}
}
JSONLexerEventType LexJSON()
{
var offset = SkipWhiteSpace( 0 );
while ( offset < source.Length )
{
var offsetStart = offset;
var character = source[ offset ];
if ( character == '{' )
{
Call( JSONLexerEventType.OBJECT_START, offset , 1 );
}
else if ( character == '}' )
{
Call( JSONLexerEventType.OBJECT_END, offset , 1 );
}
else if ( character == '[' )
{
Call( JSONLexerEventType.ARRAY_START, offset , 1 );
}
else if ( character == ']' )
{
Call( JSONLexerEventType.ARRAY_END, offset , 1 );
}
else if ( character == ',' )
{
Call( JSONLexerEventType.ARRAY_SEPERATOR, offset , 1 );
}
else if ( character == ':' )
{
Call( JSONLexerEventType.IDENTIFIER_SEPERATOR, offset , 1 );
}
else if ( character == '\"' )
{
offset = LexString( offset );
}
else if ( character == 't' )
{
offset = LexTrue( offset );
}
else if ( character == 'f' )
{
offset = LexFalse( offset );
}
else if ( character == 'n' )
{
offset = LexNull( offset );
}
else if ( System.Char.IsDigit( character ) || character == '-' )
{
offset = LexNumber( offset );
}
if ( offset == -1 )
{
return JSONLexerEventType.DONE_ERROR;
}
offset ++;
offset = SkipWhiteSpace( offset );
if ( offsetStart >= offset )
{
Call( JSONLexerEventType.ERROR_INFINITY_PREVENTION, offsetStart );
return JSONLexerEventType.DONE_ERROR;
}
}
return JSONLexerEventType.DONE_SUCCESS;
}
int SkipWhiteSpace( int offset )
{
while ( offset < source.Length && System.Char.IsWhiteSpace( source[ offset ] ) )
{
offset ++;
}
return offset;
}
bool IsIdentifier( int offset )
{
offset = SkipWhiteSpace( offset );
return offset < source.Length && source[ offset ] == ':';
}
int LexString( int offset )
{
var offsetStart = offset;
offset++;
var maxTries = 10000; var currentTries = 0;
// currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
while ( offset < source.Length )
{
currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
var character = source[ offset ];
if ( character == '\"' )
{
var previousCharacter = source[ offset - 1 ];
if ( previousCharacter != '\\' )
{
var length = ( offset - offsetStart ) + 1;
var isIdentifier = IsIdentifier( offset + 1 );
var type = isIdentifier ? JSONLexerEventType.IDENTIFIER :
JSONLexerEventType.STRING;
Call( type, offsetStart, length );
return ( offsetStart + length ) - 1;
}
}
offset ++;
}
Call( JSONLexerEventType.ERROR_UNCLOSED_STRING, offsetStart );
return -1;
}
int LexTrue( int offset )
{
if ( MatchNextThree( offset, 'r', 'u', 'e' ) )
{
Call( JSONLexerEventType.TRUE, offset, 4 );
return offset + 3;
}
Call( JSONLexerEventType.ERROR_EXPECTED_TRUE, offset );
return -1;
}
int LexFalse( int offset )
{
if ( MatchNextFour( offset, 'a', 'l', 's', 'e' ) )
{
Call( JSONLexerEventType.FALSE, offset, 5 );
return offset + 4;
}
Call( JSONLexerEventType.ERROR_EXPECTED_FALSE, offset );
return -1;
}
int LexNull( int offset )
{
if ( MatchNextThree( offset, 'u', 'l', 'l' ) )
{
Call( JSONLexerEventType.NULL, offset, 4 );
return offset + 3;
}
Call( JSONLexerEventType.ERROR_EXPECTED_NULL, offset );
return -1;
}
int LexNumber( int offset )
{
var hasDot = false;
var hasE = false;
var hasMinus = false;
var parsing = true;
var offsetStart = offset;
var maxTries = 100; var currentTries = 0;
// currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
while ( parsing && offset < source.Length )
{
currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
var character = source[ offset ];
parsing = false;
if ( System.Char.IsDigit( character ) )
{
parsing = true;
}
else if ( character == '-' && ! hasMinus )
{
hasMinus = true;
parsing = true;
}
else if ( character == '.' && ! hasDot )
{
hasDot = true;
parsing = true;
}
else if ( ( character == 'e' || character == 'E' ) && ! hasE )
{
var hasNext = offset + 2 < source.Length;
if ( hasNext )
{
var nextCharacter = source[ offset + 1 ];
hasE = nextCharacter == '+' || nextCharacter == '-';
parsing = hasE;
}
if ( parsing )
{
offset ++;
}
}
if ( parsing )
{
offset ++;
}
}
var length = ( offset - offsetStart );
Call( JSONLexerEventType.NUMBER, offsetStart, length );
return ( offsetStart + length ) - 1;
}
bool MatchNextThree( int offset, char next, char second, char third )
{
if ( offset + 3 >= source.Length ) { return false; }
if ( source[ offset + 1 ] != next ) { return false; }
if ( source[ offset + 2 ] != second ){ return false; }
if ( source[ offset + 3 ] != third ) { return false; }
return true;
}
bool MatchNextFour( int offset, char next, char second, char third, char fourth )
{
if ( offset + 4 >= source.Length ) { return false; }
if ( source[ offset + 1 ] != next ) { return false; }
if ( source[ offset + 2 ] != second ){ return false; }
if ( source[ offset + 3 ] != third ) { return false; }
if ( source[ offset + 4 ] != fourth ){ return false; }
return true;
}
}
}

View File

@ -0,0 +1,146 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Rokojori
{
public class JSONObject:JSONData
{
List<string> _keys = new List<string>();
List<JSONData> _values = new List<JSONData>();
public override JSONDataType dataType
{
get { return JSONDataType.OBJECT; }
}
public List<string> keys { get { return _keys; }}
public List<JSONData> values { get { return _values; }}
public int size { get { return _keys.Count; } }
public JSONData Get( string key )
{
var index = _keys.IndexOf( key );
return index == -1 ? null : _values[ index ];
}
public string GetString( string key ){ return Get( key ).stringValue; }
public double GetNumber( string key ){ return Get( key ).numberValue; }
public int GetInt( string key ){ return Get( key ).intValue; }
public float GetFloat( string key ){ return Get( key ).floatValue; }
public bool GetBoolean( string key ){ return Get( key ).booleanValue; }
public T GetEnum<T>( string key )
{
var value = System.Enum.Parse( typeof( T ), GetString( key ) );
return (T) value;
}
public double GetNumberOr( string key, double defaultFallback )
{
var index = _keys.IndexOf( key );
if ( index != -1 )
{
var value = _values[ index ];
return value.isNumber ? value.numberValue : defaultFallback;
}
return defaultFallback;
}
public bool Contains( string key )
{
return _keys.Contains( key );
}
public void Delete( string key )
{
var index = _keys.IndexOf( key );
if ( index != -1 )
{
_keys.RemoveAt( index );
_values.RemoveAt( index );
}
}
public void Set( string key, JSONData value )
{
var index = _keys.IndexOf( key );
if ( index == -1 )
{
_keys.Add( key );
_values.Add( value );
}
else
{
_values[ index ] = value;
}
}
public void Set( string key, double value ){ Set( key, new JSONValue( value ) ); }
public void Set( string key, string value ){ Set( key, new JSONValue( value ) ); }
public void Set( string key, bool value ){ Set( key, new JSONValue( value ) ); }
public void SetEnum<T>( string key, object value)
{
var stringValue = System.Enum.GetName( typeof( T ), value );
Set( key, stringValue );
}
public override string Stringify( StringifyOptions options )
{
options.increaseIndent();
var builder = new StringBuilder();
if ( values.Count == 0 )
{
builder.Append( "{}" );
options.decreaseIndent();
return builder.ToString();
}
builder.Append( "{\n" );
if ( values.Count > 0 )
{
builder.Append( options.indent );
JSONStringConverter.Write( builder, keys[ 0 ] );
builder.Append( ": " );
builder.Append( values[ 0 ].Stringify( options ) );
}
for ( var i = 1; i < values.Count; i++ )
{
builder.Append( ",\n" );
builder.Append( options.indent );
JSONStringConverter.Write( builder, keys[ i ] );
builder.Append( ": " );
builder.Append( values[ i ].Stringify( options ) );
}
options.decreaseIndent();
builder.Append( "\n" );
builder.Append( options.indent );
builder.Append( "}" );
return builder.ToString();
}
}
}

View File

@ -0,0 +1,227 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Godot;
namespace Rokojori
{
using LexerType = JSONLexerEventType;
public class JSONParser
{
List<JSONData> stack = new List<JSONData>();
JSONArray currentArray = null;
JSONObject currentObject = null;
JSONData current = null;
bool currentIsArray = false;
JSONData root = null;
string identifier = null;
string source = null;
bool hasError = false;
JSONObject errorReport = null;
void Reset()
{
stack = new List<JSONData>();
currentArray = null;
currentObject = null;
current = null;
currentIsArray = false;
root = null;
identifier = null;
source = null;
}
void ProcessJSONData( JSONData jsonData )
{
if ( current == null )
{
root = jsonData;
}
else if ( currentIsArray )
{
currentArray.Push( jsonData );
}
else
{
currentObject.Set( identifier, jsonData );
}
}
public static bool SHOW_DEBUG_INFO = false;
void OnParse( LexerType type, int offset, int length )
{
if ( hasError ) { return; }
if ( SHOW_DEBUG_INFO )
{
RJLog.Log(type, offset, length );
if ( offset < source.Length && length > 0 && ( offset + length ) < source.Length )
{
RJLog.Log("'" + source.Substring( offset, length ) + "'" );
}
}
switch ( type )
{
case LexerType.NUMBER:
{
var stringValue = source.Substring( offset, length );
//double numberValue = 0;
double numberValue = RegexUtility.ParseDouble( stringValue );
//System.Double.TryParse( stringValue, out numberValue );
ProcessJSONData( new JSONValue( numberValue ) );
}
break;
case LexerType.STRING:
{
var stringValue = new StringBuilder();
JSONStringConverter.Read( stringValue, source, offset, length );
ProcessJSONData( new JSONValue( stringValue.ToString() ) );
}
break;
case LexerType.IDENTIFIER:
{
var stringValue = new StringBuilder();
JSONStringConverter.Read( stringValue, source, offset, length );
identifier = stringValue.ToString();
}
break;
case LexerType.NULL:
case LexerType.TRUE:
case LexerType.FALSE:
{
JSONData jsonData = new JSONValue();
if ( LexerType.NULL != type )
{ jsonData = new JSONValue( LexerType.TRUE == type ); }
ProcessJSONData( jsonData );
}
break;
case LexerType.ARRAY_START:
case LexerType.OBJECT_START:
{
var isArrayStart = LexerType.ARRAY_START == type;
JSONData jsonData = null;
if ( isArrayStart ){ jsonData = new JSONArray(); }
else { jsonData = new JSONObject(); }
ProcessJSONData( jsonData );
current = jsonData;
if ( isArrayStart )
{
currentArray = (JSONArray) jsonData;
currentObject = null;
}
else
{
currentObject = (JSONObject) jsonData;
currentArray = null;
}
stack.Add( current );
currentIsArray = isArrayStart;
}
break;
case LexerType.ARRAY_END:
case LexerType.OBJECT_END:
{
Lists.Pop( stack );
current = Lists.Last( stack );
if ( current is JSONArray )
{
currentArray = (JSONArray) current;
currentObject = null;
currentIsArray = true;
}
else if ( current is JSONObject )
{
currentArray = null;
currentObject = (JSONObject) current;
currentIsArray = false;
}
}
break;
case LexerType.ARRAY_SEPERATOR:
case LexerType.IDENTIFIER_SEPERATOR:
{
}
break;
case LexerType.DONE_SUCCESS:
{
}
break;
// ERRORS:
default:
{
hasError = true;
CreateErrorReport( type, offset, length );
}
break;
}
}
void CreateErrorReport( LexerType type, int offset, int length )
{
var linesMapper = new TextLinesMapper();
linesMapper.Map( source );
errorReport = new JSONObject();
var end = offset + Mathf.Max( length, 0 );
var linesInfo = linesMapper.GetTextEditorInfo( offset, end );
var snippet = linesMapper.GetTextEditorSnippet( source, offset, end );
errorReport.Set( "errorType", type + "" );
errorReport.Set( "linesInfo", linesInfo );
errorReport.Set( "snippet", snippet );
}
public JSONData Parse( string source )
{
Reset();
this.source = source;
var lexer = new JSONLexer();
lexer.Lex( source, this.OnParse );
if ( hasError )
{
RJLog.Log( errorReport.Stringify() );
return null;
}
return root;
}
}
}

View File

@ -0,0 +1,215 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Rokojori
{
public class JSONStringConverter
{
static bool ContainsBackslash( string dataFromFile, int start, int length )
{
var end = start + length;
for ( var i = start; i < end; i++ )
{
if ( dataFromFile[ i ] == '\\' ) { return true; }
}
return false;
}
public static string Read( string dataFromFile )
{
var output = new StringBuilder();
Read( output, dataFromFile, 0, dataFromFile.Length );
return output.ToString();
}
public static void Read( StringBuilder output, string dataFromFile, int start, int length )
{
ReadWithoutQuotes( output, dataFromFile, start + 1, length - 2 );
}
public static void Read( StringBuilder output, string dataFromFile )
{
Read( output, dataFromFile, 0, dataFromFile.Length );
}
public static void ReadWithoutQuotes( StringBuilder output, string dataFromFile, int start, int length )
{
if ( ! JSONStringConverter.ContainsBackslash( dataFromFile, start, length ) )
{
output.Append( dataFromFile.Substring( start, length ) );
return;
}
var end = start + length;
for ( var i = start ; i < end; i++ )
{
var rawCharacter = dataFromFile[ i ];
if ( rawCharacter == '\\' )
{
if ( i == end - 1 ) { return; }
var nextRawCharacter = dataFromFile[ i + 1 ];
if ( nextRawCharacter == 'u' )
{
var hexStringStart = i + 2;
var hexString = dataFromFile.Substring( hexStringStart, 4 );
var unicodeValue = ReadUnicodeFromHexString( hexString );
output.Append( unicodeValue );
i += 5;
}
else
{
var escapedCharacter = ReadSingleEscaped( nextRawCharacter );
output.Append( escapedCharacter );
i ++;
}
}
else
{
output.Append( rawCharacter );
}
}
}
static char ReadSingleEscaped( char character )
{
switch ( character )
{
case 'b': return '\b';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
default: return character;
}
}
static char ReadUnicodeFromHexString( string hexString )
{
return (char) int.Parse( hexString, System.Globalization.NumberStyles.HexNumber );
}
static readonly char[] ESCAPE_CHARACTERS = new char[]
{
'\"', '\\', '/', '\b', '\f', '\n', '\r', '\t'
};
static readonly string[] ESCAPING_SEQUENCES = new string[]
{
"\\\"", "\\\\", "\\/", "\\b", "\\f", "\\n", "\\r", "\\t"
};
public static string Write( string dataFromMemory )
{
var output = new StringBuilder();
Write( output, dataFromMemory, 0, dataFromMemory.Length );
return output.ToString();
}
public static void Write( StringBuilder output, string dataFromMemory, int start, int length )
{
output.Append( "\"" );
WriteWithoutQuotes( output, dataFromMemory, start, length );
output.Append( "\"" );
}
public static void Write( StringBuilder output, string dataFromMemory )
{
Write( output, dataFromMemory, 0, dataFromMemory.Length );
}
public static void WriteWithoutQuotes( StringBuilder output, string dataFromMemory, int start, int length )
{
var end = start + length;
var index = IndexOfEscapeCharacter( dataFromMemory, start, end );
if ( index == -1 )
{
output.Append( dataFromMemory.Substring( start, length ) );
return;
}
for ( var i = 0; i < end; i++ )
{
var intValue = ( int ) dataFromMemory[ i ];
if ( intValue > 127 )
{
var escapedUnicodeValue = "\\u" + intValue.ToString( "x4" );
output.Append( escapedUnicodeValue );
continue;
}
var escapeIndex = GetEscapeIndex( dataFromMemory[ i ] );
if ( escapeIndex != -1 )
{
output.Append( ESCAPING_SEQUENCES[ escapeIndex ] );
}
else
{
output.Append( dataFromMemory[ i ] );
}
}
}
static int IndexOfEscapeCharacter( string dataFromMemory, int start, int end )
{
for ( var i = start; i < end; i++ )
{
var intValue = ( int ) dataFromMemory[ i ];
if ( intValue > 127 )
{
return i;
}
var escapeIndex = GetEscapeIndex( dataFromMemory[ i ] );
if ( escapeIndex != -1 )
{
return i;
}
}
return -1;
}
static int GetEscapeIndex( char c )
{
for ( int i = 0; i < ESCAPE_CHARACTERS.Length; i++ )
{
if ( ESCAPE_CHARACTERS[ i ] == c )
{
return i;
}
}
return -1;
}
}
}

View File

@ -0,0 +1,82 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Rokojori
{
public class JSONValue:JSONData
{
JSONDataType _dataType;
double _numberValue = 0;
bool _booleanValue = false;
string _stringValue = "";
public override JSONDataType dataType
{
get { return _dataType; }
}
public JSONValue(){ SetNull(); }
public JSONValue( string value ){ Set( value ); }
public JSONValue( double value ){ Set( value ); }
public JSONValue( bool value ){ Set( value ); }
private void Set( double number )
{
_numberValue = number;
_dataType = JSONDataType.NUMBER;
}
private void Set( string value )
{
_stringValue = value;
_dataType = value == null ? JSONDataType.NULL : JSONDataType.STRING;
}
private void Set( bool flag )
{
_booleanValue = flag;
_dataType = JSONDataType.BOOLEAN;
}
private void SetNull()
{
_dataType = JSONDataType.NULL;
}
public double GetNumberValue(){ return _numberValue; }
public float GetFloatValue() { return (float) _numberValue; }
public int GetIntValue(){ return (int) System.Math.Round( _numberValue ); }
public bool GetBooleanValue(){ return _booleanValue; }
public string GetStringValue(){ return _stringValue; }
public T GetEnumFromString<T>( T enumValue ) where T : System.Enum
{
//System.Enum.TryParse( _stringValue, out T enumValue);
return enumValue;
}
public override string Stringify( StringifyOptions options )
{
if ( JSONDataType.STRING == _dataType )
{
var output = new StringBuilder();
JSONStringConverter.Write( output, _stringValue, 0, _stringValue.Length );
return output.ToString();
}
else if ( JSONDataType.NUMBER == _dataType )
{
return RegexUtility.WriteDouble( _numberValue );
}
else if ( JSONDataType.BOOLEAN == _dataType )
{
return _booleanValue ? "true" : "false";
}
return "null";
}
}
}

View File

@ -0,0 +1,40 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System;
using System.Numerics;
namespace Rokojori
{
public class BigIntegerSerializer: CustomSerializer
{
public override List<Type> HandlingTypes()
{
return new List<Type>(){ typeof( BigInteger ) };
}
public override JSONData Serialize( object value )
{
var bigInteger = (BigInteger) value;
return new JSONValue( bigInteger.ToString( "R" ) );
}
public override void Deserialize( JSONData data, Reference referenceRelation )
{
if ( referenceRelation.target == null )
{
return;
}
var bigInteger = BigInteger.Parse( data.stringValue );
referenceRelation.AssignValue( bigInteger );
}
}
}

View File

@ -0,0 +1,54 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System;
using Godot;
namespace Rokojori
{
public class ColorSerializer: CustomSerializer
{
public override List<Type> HandlingTypes()
{
return new List<Type>(){ typeof( Color ) };
}
public override JSONData Serialize( object value )
{
var color = (Color) value;
var jsonArray = new JSONArray();
jsonArray.Push( color.R );
jsonArray.Push( color.G );
jsonArray.Push( color.B );
jsonArray.Push( color.A );
return jsonArray;
}
public override void Deserialize( JSONData data, Reference referenceRelation )
{
if ( referenceRelation.target == null )
{
return;
}
var array = data.AsArray();
var color = new Color(
array.Get( 0 ).floatValue,
array.Get( 1 ).floatValue,
array.Get( 2 ).floatValue,
array.Get( 3 ).floatValue
);
referenceRelation.AssignValue( color );
}
}
}

View File

@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
namespace Rokojori
{
public abstract class CustomSerializer
{
public abstract List<Type> HandlingTypes();
public abstract JSONData Serialize( object value );
public abstract void Deserialize( JSONData data, Reference reference );
}
}

View File

@ -0,0 +1,42 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System;
namespace Rokojori
{
public class DateTimeSerializer: CustomSerializer
{
public override List<Type> HandlingTypes()
{
return new List<Type>(){ typeof( DateTime ) };
}
public override JSONData Serialize( object value )
{
var dateTime = (DateTime) value;
return new JSONValue( dateTime.ToString( "o" ) );
}
public override void Deserialize( JSONData data, Reference referenceRelation )
{
if ( referenceRelation.target == null )
{
return;
}
var dateTime = System.DateTime.Parse( data.stringValue,
CultureInfo.InvariantCulture,
DateTimeStyles.RoundtripKind );
referenceRelation.AssignValue( dateTime );
}
}
}

View File

@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
using System.Numerics;
namespace Rokojori
{
public class JSONAlwaysProcessable: System.Attribute
{
}
}

View File

@ -0,0 +1,397 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
namespace Rokojori
{
public class JSONDeserializer
{
JSONSerializationSettings settings = new JSONSerializationSettings();
Dictionary<Type,CustomSerializer> customSerializers = null;
public JSONDeserializer ( JSONSerializationSettings settings )
{
this.settings = settings == null ? new JSONSerializationSettings() : settings;
}
public T Deserialize<T>( string data ) where T:new()
{
return Deserialize<T>( JSON.Parse( data ).AsObject() );
}
public T Deserialize<T>( JSONObject jsonObject ) where T:new()
{
Initialize();
var output = new T();
var keys = jsonObject.keys;
for ( int i = 0; i < keys.Count; i++)
{
var key = keys[ i ];
DeserializeMemberField( jsonObject.Get( key ), output, key );
}
return output;
}
public void Deserialize<T>( string data, T output )
{
Deserialize<T>( JSON.Parse( data ).AsObject(), output );
}
public void Deserialize<T>( JSONObject jsonObject, T output )
{
Initialize();
var keys = jsonObject.keys;
for ( int i = 0; i < keys.Count; i++)
{
var key = keys[ i ];
DeserializeMemberField( jsonObject.Get( key ), output, key );
}
}
void DeserializeMemberField( JSONData data, object parent, string name )
{
if ( data.isNull )
{
SetMemberValue( parent, name, null );
return;
}
var type = parent.GetType();
var field = type.GetField( name );
if ( field == null )
{
RJLog.Log( "Field not present: " + name );
return;
}
var reference = new Reference( parent, name );
var fieldType = field.FieldType;
AssignValue( data, reference, fieldType );
}
void DeserializeListField( JSONData data, IList parent, int index )
{
if ( data.isNull )
{
SetListValue( parent, index, null );
return;
}
var type = parent.GetType();
var reference = new Reference( parent, index );
var listType = type.GetGenericArguments()[ 0 ];
AssignValue( data, reference, listType );
}
void DeserializeDictionaryField( JSONData data, IDictionary parent, string key, int index )
{
var type = parent.GetType();
var isIndexed = type.GetGenericArguments()[ 0 ] == typeof( int );
if ( data.isNull )
{
if ( isIndexed )
{
SetDictionaryValue( parent, index, null );
}
else
{
SetDictionaryValue( parent, key, null );
}
return;
}
var reference = isIndexed ? new Reference( parent, index ) : new Reference( parent, key );
var valueType = type.GetGenericArguments()[ 1 ];
AssignValue( data, reference, valueType );
}
void AssignValue( JSONData data, Reference reference, Type type )
{
var customSerializer = GetCustomDeserializer( type );
if ( customSerializer != null )
{
customSerializer.Deserialize( data, reference );
return;
}
if ( typeof( int ) == type )
{
SetNumber( data, reference, INT );
}
else if ( typeof( float ) == type )
{
SetNumber( data, reference, FLOAT );
}
else if ( typeof( double ) == type )
{
SetNumber( data, reference, DOUBLE );
}
else if ( typeof( bool ) == type )
{
if ( ! data.isBoolean )
{
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
return;
}
reference.AssignValue( data.booleanValue );
}
else if ( type.IsEnum )
{
reference.AssignValue( data.enumValue( type ) );
}
else if ( typeof( string ) == type )
{
if ( ! data.isString )
{
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
return;
}
reference.AssignValue( data.stringValue );
}
else if ( ReflectionHelper.IsList( type ) )
{
if ( ! data.isArray )
{
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
return;
}
reference.AssignValue( CreateList( data, type ) );
}
else if ( IsSerializableDictionary( type ) )
{
if ( ! data.isObject )
{
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
return;
}
reference.AssignValue( CreateDictionary( data, type ) );
}
else
{
if ( ! data.isObject )
{
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
return;
}
reference.AssignValue( CreateObject( data, type ) );
}
}
IList CreateList( JSONData data, Type fieldType )
{
var array = data.AsArray();
var list = (IList) Activator.CreateInstance( fieldType );
for ( int i = 0 ; i < array.size; i++ )
{
var jsonValue = array.Get( i );
DeserializeListField( jsonValue, list, i );
}
return list;
}
IDictionary CreateDictionary( JSONData data, Type fieldType )
{
var map = data.AsObject();
var dict = (IDictionary) Activator.CreateInstance( fieldType );
var keys = map.keys;
var isIndexed = dict.GetType().GetGenericArguments()[ 0 ] == typeof( int );
if ( isIndexed )
{
for ( int i = 0 ; i < keys.Count; i++ )
{
var jsonValue = map.Get( keys[ i ] );
var index = RegexUtility.ParseInt( keys[ i ] );
DeserializeDictionaryField( jsonValue, dict, "", index );
}
}
else
{
for ( int i = 0 ; i < keys.Count; i++ )
{
var jsonValue = map.Get( keys[ i ] );
DeserializeDictionaryField( jsonValue, dict, keys[ i ], 0 );
}
}
return dict;
}
object CreateObject( JSONData data, Type type )
{
var output = Activator.CreateInstance( type );
var jsonObject = data.AsObject();
var keys = jsonObject.keys;
for ( int i = 0; i < keys.Count; i++)
{
var key = keys[ i ];
DeserializeMemberField( jsonObject.Get( key ), output, key );
}
return output;
}
void SetMemberValue( object instance, string name, object value )
{
ReflectionHelper.SetMemberValue( instance, name, value );
}
void SetListValue( IList list, int index, object value )
{
list[ index ] = value;
}
void SetDictionaryValue( IDictionary dictionary, string index, object value )
{
dictionary[ index ] = value;
}
void SetDictionaryValue( IDictionary dictionary, int index, object value )
{
dictionary[ index ] = value;
}
const int INT = 0;
const int FLOAT = 1;
const int DOUBLE = 2;
void SetNumber( JSONData data, Reference reference, int numberType )
{
if ( ! settings.saveNumbersWithType )
{
if ( ! data.isNumber )
{
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
return;
}
switch ( numberType )
{
case INT:{ reference.AssignValue( data.intValue ); return; }
case FLOAT:{ reference.AssignValue( data.floatValue ); return; }
case DOUBLE:{ reference.AssignValue( data.numberValue ); return; }
}
RJLog.Log( "Unknown number type: " + reference.GetInfo() + ">>" + data );
return;
}
if ( ! data.isObject )
{
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
return;
}
var objectValue = data.AsObject();
if ( ! objectValue.Contains( JSONSerializationSettings.NUMBER_VALUE ) )
{
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
return;
}
switch ( numberType )
{
case INT:
{
var value = objectValue.Get( JSONSerializationSettings.NUMBER_VALUE ).intValue;
reference.AssignValue( value );
}
break;
case FLOAT:
{
var value = objectValue.Get( JSONSerializationSettings.NUMBER_VALUE ).floatValue;
reference.AssignValue( value );
}
break;
case DOUBLE:
{
var value = objectValue.Get( JSONSerializationSettings.NUMBER_VALUE ).numberValue;
reference.AssignValue( value );
}
break;
}
}
void Initialize()
{
if ( customSerializers != null ){ return ;}
customSerializers = new Dictionary<Type,CustomSerializer>();
settings.customSerializers.ForEach( s => AddSerializer( s ) );
JSONSerializationSettings.defaultSerializers.ForEach( s => AddSerializer( s ) );
}
void AddSerializer( CustomSerializer serializer )
{
var types = serializer.HandlingTypes();
types.ForEach( type => AddSerializerForType( type, serializer ) );
}
void AddSerializerForType( Type type, CustomSerializer serializer )
{
if ( customSerializers.ContainsKey( type ) )
{
return;
}
customSerializers[ type ] = serializer;
}
CustomSerializer GetCustomDeserializer( Type type )
{
if ( customSerializers.ContainsKey( type ) )
{
return customSerializers[ type ];
}
return null;
}
static bool IsSerializableDictionary( Type type )
{
return JSONSerializationSettings.IsSerializableDictionary( type );
}
}
}

View File

@ -0,0 +1,429 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
using System.Linq;
using System.Numerics;
namespace Rokojori
{
public class JSONSerializer
{
JSONSerializationSettings settings = new JSONSerializationSettings();
HashSet<object> processedObjects = new HashSet<object>();
HashSet<Type> alwaysReprocessTypes = new HashSet<Type>()
{
typeof( DateTime ), typeof( BigInteger )
};
public JSONSerializer ( JSONSerializationSettings settings = null )
{
this.settings = settings == null ? new JSONSerializationSettings() : settings;
}
Dictionary<Type,CustomSerializer> customSerializers = null;
public string Serialize( object value )
{
Initialize();
if ( value == null )
{
return JSON.Stringify( new JSONValue() );
}
else if ( value is int )
{
return JSON.Stringify( CreateNumber( (int) value ) );
}
else if ( value is double )
{
return JSON.Stringify( CreateNumber( (double) value ) );
}
else if ( value is float )
{
return JSON.Stringify( CreateNumber( (float) value ) );
}
else if ( value is string )
{
return JSON.Stringify( new JSONValue( (string) value ) );
}
else if ( value is bool )
{
return JSON.Stringify( new JSONValue( (bool) value ) );
}
else if ( ReflectionHelper.IsList( value ) )
{
return JSON.Stringify( CreateArray( value as IList ) );
}
else if ( IsSerializableDictionary( value ) )
{
return JSON.Stringify( CreateMapLikeObject( value as IDictionary ) );
}
else
{
var serializer = GetCustomSerializer( value );
if ( serializer != null )
{
return JSON.Stringify( serializer.Serialize( value ) );
}
else
{
return JSON.Stringify( CreateObject( value ) );
}
}
}
void Initialize()
{
processedObjects.Clear();
if ( customSerializers != null ){ return ;}
customSerializers = new Dictionary<Type,CustomSerializer>();
settings.customSerializers.ForEach( s => AddSerializer( s ) );
JSONSerializationSettings.defaultSerializers.ForEach( s => AddSerializer( s ) );
}
void AddSerializer( CustomSerializer serializer )
{
var types = serializer.HandlingTypes();
types.ForEach( type => AddSerializerForType( type, serializer ) );
}
void AddSerializerForType( Type type, CustomSerializer serializer )
{
if ( customSerializers.ContainsKey( type ) )
{
return;
}
customSerializers[ type ] = serializer;
}
CustomSerializer GetCustomSerializer( object value )
{
if ( value == null ){ return null; }
var type = value.GetType();
if ( customSerializers.ContainsKey( type ) )
{
return customSerializers[ type ];
}
return null;
}
static bool IsSerializableDictionary( object value )
{
return JSONSerializationSettings.IsSerializableDictionary( value );
}
JSONData CreateNumber( int value )
{
if ( settings.saveNumbersWithType )
{
var jsonObject = new JSONObject();
jsonObject.Set( JSONSerializationSettings.NUMBER_TYPE, JSONSerializationSettings.INT_NUMBER_TYPE );
jsonObject.Set( JSONSerializationSettings.NUMBER_VALUE, value );
return jsonObject;
}
return new JSONValue( value );
}
JSONData CreateNumber( float value )
{
if ( settings.saveNumbersWithType )
{
var jsonObject = new JSONObject();
jsonObject.Set( JSONSerializationSettings.NUMBER_TYPE, JSONSerializationSettings.FLOAT_NUMBER_TYPE );
jsonObject.Set( JSONSerializationSettings.NUMBER_VALUE, value );
return jsonObject;
}
return new JSONValue( value );
}
JSONData CreateNumber( double value )
{
if ( settings.saveNumbersWithType )
{
var jsonObject = new JSONObject();
jsonObject.Set( JSONSerializationSettings.NUMBER_TYPE, JSONSerializationSettings.DOUBLE_NUMBER_TYPE );
jsonObject.Set( JSONSerializationSettings.NUMBER_VALUE, value );
return jsonObject;
}
return new JSONValue( value );
}
bool IsProcessableObject( object value )
{
if (System.Attribute.IsDefined( value.GetType(), typeof(JSONAlwaysProcessable) ) )
{
return true;
}
if ( alwaysReprocessTypes.Contains( value.GetType() ) )
{
return true;
}
if ( processedObjects.Contains( value ) )
{
RJLog.Error( "Cycle detected: " + value );
var interfaces = typeof( object ).GetInterfaces();
for ( int i = 0; i < interfaces.Length; i++ )
{
RJLog.Error( "Interfaces[" + i + "]" + interfaces[ i ].Name );
}
if ( settings.throwErrorOnCycles )
{
throw new System.Exception( "Cycle detected: " + value );
}
return false;
}
processedObjects.Add( value );
return true;
}
JSONData CreateArray( IList value )
{
if ( ! IsProcessableObject( value ) )
{
return new JSONValue();
}
var jsonArray = new JSONArray();
//RJLog.Log(value );
for ( int i = 0; i < value.Count; i++ )
{
AssignArrayMember( jsonArray, i, value[ i ] );
}
return jsonArray;
}
JSONData CreateMapLikeObject( IDictionary value )
{
if ( ! IsProcessableObject( value ) )
{
return new JSONValue();
}
var jsonObject = new JSONObject();
foreach ( var key in value.Keys )
{
AssignObjectMember( jsonObject, key + "", value[ key ] );
}
return jsonObject;
}
JSONData CreateObject( object obj )
{
if ( ! IsProcessableObject( obj ) )
{
return new JSONValue();
}
var type = obj.GetType();
var fields = type.GetFields();
var jsonObject = new JSONObject();
foreach ( var f in fields )
{
if ( f.IsStatic )
{
continue;
}
var name = f.Name;
var value = f.GetValue( obj );
if ( value == null )
{
jsonObject.Set( name, new JSONValue() );
}
else if ( value is int )
{
jsonObject.Set( name, CreateNumber( (int) value ) );
}
else if ( value is double )
{
jsonObject.Set( name, CreateNumber( (double) value ) );
}
else if ( value is float )
{
jsonObject.Set( name, CreateNumber( (float) value ) );
}
else if ( value is string )
{
jsonObject.Set( name, (string) value );
}
else if ( value is bool )
{
jsonObject.Set( name, (bool) value );
}
else if ( value is Enum )
{
jsonObject.Set( name, (int) value );
}
else if ( ReflectionHelper.IsList( value ) )
{
jsonObject.Set( name, CreateArray( value as IList ) );
}
else if ( IsSerializableDictionary( value ) )
{
jsonObject.Set( name, CreateMapLikeObject( value as IDictionary ) );
}
else
{
var exporter = GetCustomSerializer( value );
if ( exporter != null )
{
jsonObject.Set( name, exporter.Serialize( value ) );
}
else
{
jsonObject.Set( name, CreateObject( value ) );
}
}
}
return jsonObject;
}
void AssignArrayMember( JSONArray jsonArray, int index, object value )
{
if ( value == null )
{
jsonArray.Set( index, new JSONValue() );
}
else if ( value is int )
{
jsonArray.Set( index, CreateNumber( (int) value ) );
}
else if ( value is double )
{
jsonArray.Set( index, CreateNumber( (double) value ) );
}
else if ( value is float )
{
jsonArray.Set( index, CreateNumber( (float) value ) );
}
else if ( value is string )
{
jsonArray.Set( index, (string) value );
}
else if ( value is bool )
{
jsonArray.Set( index, (bool) value );
}
else if ( ReflectionHelper.IsList( value ) )
{
jsonArray.Set( index, CreateArray( value as IList) );
}
else if ( IsSerializableDictionary( value ) )
{
jsonArray.Set( index, CreateMapLikeObject( value as IDictionary ) );
}
else
{
var exporter = GetCustomSerializer( value );
if ( exporter != null )
{
jsonArray.Set( index, exporter.Serialize( value ) );
}
else
{
jsonArray.Set( index, CreateObject( value ) );
}
}
}
void AssignObjectMember( JSONObject jsonObject, string name, object value )
{
if ( value == null )
{
jsonObject.Set( name, new JSONValue() );
}
else if ( value is int )
{
jsonObject.Set( name, CreateNumber( (int) value ) );
}
else if ( value is double )
{
jsonObject.Set( name, CreateNumber( (double) value ) );
}
else if ( value is float )
{
jsonObject.Set( name, CreateNumber( (float) value ) );
}
else if ( value is string )
{
jsonObject.Set( name, (string) value );
}
else if ( value is bool )
{
jsonObject.Set( name, (bool) value );
}
else if ( ReflectionHelper.IsList( value ) )
{
jsonObject.Set( name, CreateArray( value as IList ) );
}
else if ( IsSerializableDictionary( value ) )
{
jsonObject.Set( name, CreateMapLikeObject( value as IDictionary ) );
}
else
{
var exporter = GetCustomSerializer( value );
if ( exporter != null )
{
jsonObject.Set( name, exporter.Serialize( value ) );
}
else
{
jsonObject.Set( name, CreateObject( value ) );
}
}
}
}
}

View File

@ -0,0 +1,66 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
namespace Rokojori
{
public class JSONSerializationSettings
{
public static readonly string NUMBER_TYPE = "number-type";
public static readonly string NUMBER_VALUE = "number-value";
public static readonly string INT_NUMBER_TYPE = "int";
public static readonly string FLOAT_NUMBER_TYPE = "float";
public static readonly string DOUBLE_NUMBER_TYPE = "double";
static List<Type> serializableDictionaryKeyTypes = new List<Type>()
{
typeof( string ), typeof( int )
};
public static readonly List<CustomSerializer> defaultSerializers = new List<CustomSerializer>()
{
new DateTimeSerializer(),
new BigIntegerSerializer()
};
public static bool IsSerializableDictionary( Type type )
{
if ( ! ReflectionHelper.IsDictionary( type ) )
{
return false;
}
var keyType = type.GetGenericArguments()[ 0 ];
return serializableDictionaryKeyTypes.IndexOf( keyType ) != -1;
}
public static bool IsSerializableDictionary( object value )
{
if ( ! ReflectionHelper.IsDictionary( value ) )
{
return false;
}
var keyType = value.GetType().GetGenericArguments()[ 0 ];
return serializableDictionaryKeyTypes.IndexOf( keyType ) != -1;
}
public List<CustomSerializer> customSerializers = new List<CustomSerializer>();
public bool saveNumbersWithType = false;
public bool throwErrorOnCycles = false;
}
}

View File

@ -0,0 +1,168 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System;
using Godot;
namespace Rokojori
{
public enum ReferenceType
{
MEMBER_REFERENCE,
ARRAY_LIST_REFERENCE,
MAP_DICTIONARY_REFERENCE
}
public class Reference
{
public ReferenceType type;
public object target;
public int index;
public string name;
public string GetInfo()
{
var accessorInfo = "";
if ( ReferenceType.ARRAY_LIST_REFERENCE == type )
{
if ( target is JSONArray )
{
accessorInfo += "JSONArray[ " + index + " ]";
}
else if ( target is IList )
{
accessorInfo += "List[ " + index + " ]";
}
else
{
accessorInfo += "List/Array[ " + index + " ]";
}
}
else if ( ReferenceType.MEMBER_REFERENCE == type )
{
accessorInfo += "Member[ " + name + " ]";
}
else if ( ReferenceType.MAP_DICTIONARY_REFERENCE == type )
{
if ( JSONSerializationSettings.IsSerializableDictionary( target ) )
{
var keyType = target.GetType().GetGenericArguments()[ 0 ];
if ( typeof( int ) == keyType )
{
accessorInfo += "Dictionary[ " + index + " ]";
}
else if ( typeof( string ) == keyType )
{
accessorInfo += "Dictionary[ " + name + " ]";
}
}
else if ( target is JSONObject )
{
accessorInfo += "JSONObject[ " + name + " ]";
}
else
{
accessorInfo += "Map/Dictionary[ " + index + " ]";
}
}
return accessorInfo + "@" + target;
}
public void AssignValue( object value )
{
if ( ReferenceType.ARRAY_LIST_REFERENCE == type )
{
if ( target is IList )
{
var list = ( (IList) target );
var missing = Mathf.Max( 0, index - ( list.Count - 1 ) );
for ( int i = 0; i < missing; i++ )
{
list.Add( null );
}
list[ index ] = value;
}
else if ( target is JSONArray )
{
( (JSONArray) target ).Set( index, (JSONData) value );
}
}
else if ( ReferenceType.MEMBER_REFERENCE == type )
{
if ( target is JSONObject )
{
( (JSONObject) target ).Set( name, (JSONData) value );
}
else
{
ReflectionHelper.SetMemberValue( target, name, value );
}
}
else if ( ReferenceType.MAP_DICTIONARY_REFERENCE == type )
{
if ( target is JSONObject )
{
( (JSONObject) target ).Set( name, (JSONData) value );
}
else if ( JSONSerializationSettings.IsSerializableDictionary( target ) )
{
if ( typeof( string ) == target.GetType().GetGenericArguments()[ 0 ] )
{
( (IDictionary) target )[ name ] = value;
}
else
{
( (IDictionary) target )[ index ] = value;
}
}
}
}
public Reference( object target, int index )
{
this.target = target;
this.index = index;
if ( target is JSONObject || JSONSerializationSettings.IsSerializableDictionary( target ) )
{
type = ReferenceType.MAP_DICTIONARY_REFERENCE;
}
else
{
type = ReferenceType.ARRAY_LIST_REFERENCE;
}
}
public Reference( object target, string name )
{
this.target = target;
this.name = name;
if ( target is JSONObject || JSONSerializationSettings.IsSerializableDictionary( target ) )
{
type = ReferenceType.MAP_DICTIONARY_REFERENCE;
}
else
{
type = ReferenceType.MEMBER_REFERENCE;
}
}
}
}

View File

@ -0,0 +1,126 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Rokojori
{
public class Lexer
{
Dictionary<string,List<LexerMatcher>> _modes = new Dictionary<string, List<LexerMatcher>>();
bool _hasError =false;
public bool hasError => _hasError;
void AddMatcher( LexerMatcher matcher )
{
var list = _modes.ContainsKey( matcher.mode ) ? _modes[ matcher.mode ] : null;
if ( list == null )
{
list = new List<LexerMatcher>();
_modes[ matcher.mode ] = list;
}
list.Add( matcher );
}
public void AddAllMatchers( params LexerMatcher[] matchers )
{
for ( int i = 0; i < matchers.Length; i++ )
{
AddMatcher( matchers[ i ] );
}
}
public void AddMatcher( string type, Regex regex, string mode = "", string nextMode = null )
{
AddMatcher( new LexerMatcher( type, regex, mode, nextMode ) );
}
public void AddMatcher( string type, string regex, string mode = "", string nextMode = null )
{
AddMatcher( type, new Regex( regex ), mode, nextMode );
}
public void Lex( string source, System.Action<LexerEvent> callback, int offset = 0, string mode = "" )
{
var ERROR_FLAG = -1;
var DONE_FLAG = -2;
var lexerEvent = new LexerEvent( "", 0, -2 );
while ( offset < source.Length )
{
if ( ! _modes.ContainsKey( mode ) )
{
var errorMessage = "@Lexer-Error. Mode not found: '" + mode + "'";
RJLog.Log( errorMessage, "@", offset );
lexerEvent.set( errorMessage, offset, ERROR_FLAG );
_hasError = true;
callback( lexerEvent );
return;
}
var matchers = _modes[ mode ];
var foundSomething = false;
for ( var i = 0; i < matchers.Count; i++ )
{
var matcher = matchers[ i ];
var matchLength = matcher.MatchLength( source, offset );
if ( matchLength > 0 )
{
lexerEvent.set( matcher.type, offset, matchLength );
//Logs.Log(matcher.type, ">>", "'"+source.Substring( offset, matchLength )+"'", "@", offset, matchLength );
callback( lexerEvent );
foundSomething = true;
i = matchers.Count;
if ( matcher.nextMode != null )
{
mode = matcher.nextMode;
}
offset += matchLength;
}
}
if ( ! foundSomething )
{
var errorMessage = "@Lexer-Error. No match: '" + mode + "'";
RJLog.Log(errorMessage, "@", offset );
lexerEvent.set( errorMessage, offset, ERROR_FLAG );
_hasError = true;
callback( lexerEvent );
return;
}
}
lexerEvent.set( mode, offset, DONE_FLAG );
callback( lexerEvent );
}
public List<LexerEvent> LexToList( string source, int offset = 0, string mode = "" )
{
var list = new List<LexerEvent>();
Lex(
source,
( LexerEvent token )=>
{
list.Add( token.Copy() );
},
offset, mode
);
return list;
}
}
}

View File

@ -0,0 +1,69 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Rokojori
{
public class LexerEvent
{
string _type;
int _offset;
int _length;
string _match;
public string match => _match;
public LexerEvent( string type, int offset, int length )
{
set( type, offset, length );
}
public string type { get { return _type; } }
public int offset { get { return _offset; } }
public int length { get { return _length; } }
public bool isError
{
get { return this.length == -1; }
}
public bool isDone
{
get { return this.length == -2; }
}
public void set( string type, int offset, int length )
{
this._type = type;
this._offset = offset;
this._length = length;
}
public LexerEvent Copy()
{
return new LexerEvent( _type, _offset, _length );
}
public int end
{
get { return this._offset + _length; }
}
public override string ToString()
{
return "Token{ '" + type + "' (" + offset + "-" + end + ") }";
}
public void GrabMatch( string source )
{
if ( _length < 0 )
{
return;
}
_match = source.Substring( offset, length );
}
}
}

View File

@ -0,0 +1,47 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Rokojori
{
public class CSharpLexer:Lexer
{
public static List<LexerEvent> Lex( string source )
{
var lexer = new CSharpLexer();
var events = lexer.LexToList( source );
if ( lexer.hasError )
{
return null;
}
events.ForEach( ev => { ev.GrabMatch( source ); } );
return events;
}
public CSharpLexer()
{
AddAllMatchers(
LexerMatcherLibrary.SINGLE_LINE_COMMENT_MATCHER,
LexerMatcherLibrary.MULTI_LINE_COMMENT_MATCHER,
LexerMatcherLibrary.DOUBLE_QUOTED_STRING_MATCHER,
LexerMatcherLibrary.SINGLE_QUOTED_STRING_MATCHER,
LexerMatcherLibrary.C_INSTRUCTION_MATCHER,
LexerMatcherLibrary.NUMBER_MATCHER,
LexerMatcherLibrary.NULL_MATCHER,
LexerMatcherLibrary.BOOL_MATCHER,
LexerMatcherLibrary.BREAK_MATCHER,
LexerMatcherLibrary.WHITESPACE_MATCHER,
LexerMatcherLibrary.LOGIC_MATCHER,
LexerMatcherLibrary.BRACKET_MATCHER,
LexerMatcherLibrary.ACCESS_MODIFIER_MATCHER,
LexerMatcherLibrary.CLASS_MATCHER,
LexerMatcherLibrary.OPERATOR_MATCHER,
LexerMatcherLibrary.CFUNCTION_MATCHER,
LexerMatcherLibrary.CWORD_MATCHER,
LexerMatcherLibrary.ANY_SYMBOL_MATCHER
);
}
}
}

View File

@ -0,0 +1,70 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Rokojori
{
public class LexerMatcher
{
string _mode;
public string mode => _mode;
string _type;
public string type => _type;
string _nextMode;
public string nextMode => _nextMode;
Regex _matcher;
Regex matcher
{
get { return this._matcher; }
}
public bool Matches( LexerEvent le )
{
return type == le.type;
}
public LexerMatcher( string type, Regex matcher, string mode = "", string nextMode = "" )
{
_type = type;
_mode = mode;
_nextMode = nextMode;
_matcher = RegexUtility.MakeSticky( matcher );
}
public LexerMatcher( string type, string matcher, string mode = "", string nextMode = "" )
{
_type = type;
_mode = mode;
_nextMode = nextMode;
_matcher = RegexUtility.MakeSticky( new Regex( matcher ) );
}
public LexerMatcher CloneWithMode( string mode, string nextMode )
{
return new LexerMatcher( _type, _matcher, mode, nextMode );
}
public int MatchLength( string source, int offset )
{
var match = matcher.Match( source, offset );
if ( match.Success && match.Index != offset )
{
var message = "Illegal match index! Not a sticky matcher: " + matcher + ".";
message += "Offset: " + offset + " Matched Index: " + match.Index ;
throw new System.Exception( message );
}
if ( match.Success )
{
return match.Groups[ 0 ].Length;
}
return -1;
}
}
}

View File

@ -0,0 +1,100 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Rokojori
{
public class LexerMatcherLibrary
{
public static readonly LexerMatcher CWORD_MATCHER =
new LexerMatcher( "CWORD", @"[a-zA-Z_]\w*" );
public static readonly LexerMatcher CFUNCTION_MATCHER =
new LexerMatcher( "CFUNCTION", @"[a-zA-Z_]\w*(?=\s*\()" );
// public static readonly LexerMatcher LexerMatcherCLASSNAME_MATCHER =
// new LexerMatcher( "CLASSNAME", @"(?<=(?:(?:class|interface|struct)\s+))[a-zA-Z_]\w*" );
public static readonly LexerMatcher JSWORD_MATCHER =
new LexerMatcher( "JSWORD", @"[a-zA-Z_\$]\w*" );
public static readonly LexerMatcher PHPWORD_MATCHER =
new LexerMatcher( "PHPWORD", @"\$?[a-zA-Z_]\w*" );
public static readonly LexerMatcher CSS_CLASS_SELECTOR_MATCHER =
new LexerMatcher( "CSS_CLASS_SELECTOR", @"\.[a-zA-Z_](\w|\-)*" );
public static readonly LexerMatcher CSS_ID_SELECTOR_MATCHER =
new LexerMatcher( "CSS_ID_SELECTOR", @"\#[a-zA-Z_](\w|\-)*" );
public static readonly LexerMatcher CSS_WORD_MATCHER =
new LexerMatcher( "CSS_WORD", @"[a-zA-Z_](\w|\-)*" );
public static readonly LexerMatcher HTML_CUSTOM_ELEMENT_MATCHER =
new LexerMatcher( "HTML_CUSTOM_ELEMENT", @"[a-zA-Z]\w*\-(\w|\-)+" );
public static readonly LexerMatcher DOUBLE_QUOTED_STRING_MATCHER =
new LexerMatcher( "DOUBLE_QUOTED_STRING", "\"(?:[^\"\\\\]|\\\\.)*\"" );
public static readonly LexerMatcher SINGLE_QUOTED_STRING_MATCHER =
new LexerMatcher( "SINGLE_QUOTED_STRING", @"'(?:[^'\\]|\\.)*'" );
public static readonly LexerMatcher NUMBER_MATCHER =
new LexerMatcher( "NUMBER", @"(?=\.\d|\d)(?:\d+)?(?:\.?\d*)(?:[eE][+-]?\d+)?" );
public static readonly LexerMatcher WHITESPACE_MATCHER =
new LexerMatcher( "WHITESPACE", @"\s+" );
public static readonly LexerMatcher BREAK_MATCHER =
new LexerMatcher( "BREAK", @"(\r\n|\r|\n)" );
public static readonly LexerMatcher NULL_MATCHER =
new LexerMatcher( "NULL", "null" );
public static readonly LexerMatcher BOOL_MATCHER =
new LexerMatcher( "BOOL", "true|false" );
public static readonly LexerMatcher LOGIC_MATCHER =
new LexerMatcher( "LOGIC", "if|else|switch|do|while|for|break|continue|return" );
public static readonly LexerMatcher OPERATOR_MATCHER =
new LexerMatcher( "OPERATOR", "(?:\\=\\=)|(?:\\+\\+)|(?:\\-\\-)|\\+|\\-|\\*|\\/|\\^|\\||\\~|\\&|\\%|\\<|\\>|\\=|\\!|\\.|\\:|\\,|\\;" );
public static readonly LexerMatcher BRACKET_MATCHER =
new LexerMatcher( "BRACKET", @"\(|\)|\[|\]|\{|\}" );
public static readonly LexerMatcher BLOCKSTART_MATCHER =
new LexerMatcher( "BLOCKSTART", @"\{" );
public static readonly LexerMatcher BLOCKEND_MATCHER =
new LexerMatcher( "BLOCKEND", @"\}" );
public static readonly LexerMatcher CLASS_MATCHER =
new LexerMatcher( "CLASS", @"\bclass\b" );
public static readonly LexerMatcher ACCESS_MODIFIER_MATCHER =
new LexerMatcher( "ACCESS_MODIFIER", @"\b(?:public|protected|private)\b" );
public static readonly LexerMatcher SINGLE_LINE_COMMENT_MATCHER =
new LexerMatcher( "SINGLE_LINE_COMMENT", @"//.*" );
public static readonly LexerMatcher C_INSTRUCTION_MATCHER =
new LexerMatcher( "C_INSTRUCTION", @"\#.*" );
public static readonly LexerMatcher MULTI_LINE_COMMENT_MATCHER =
new LexerMatcher( "MULTI_LINE_COMMENT", @"\/\*(.|(\r\n|\r|\n))*?\*\/" );
public static readonly LexerMatcher ANY_SYMBOL_MATCHER =
new LexerMatcher( "ANY_SYMBOL", @"." );
public static readonly LexerMatcher HASH_TAG =
new LexerMatcher( "HASH_TAG", @"\#(\w|-|\d)+" );
public static readonly LexerMatcher URL =
new LexerMatcher( "URL", @"https?\:\/\/(\w|\.|\-|\?|\=|\+|\/)+" );
}
}

View File

@ -0,0 +1,117 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;
using System.Globalization;
using Godot;
namespace Rokojori
{
public class RegexBuilder
{
List<string> _parts = new List<string>();
List<string> _flags = new List<string>();
public static RegexBuilder Create()
{
var rb = new RegexBuilder();
return rb;
}
public RegexBuilder this[ string key ]
{
get { _parts.Add( RegexUtility.EscapeForRegex( key ) ); return this; }
}
public RegexBuilder _
{
get
{
_parts.Add( @"\s*" );
return this;
}
}
public RegexBuilder __
{
get
{
_parts.Add( @"\s+" );
return this;
}
}
public RegexBuilder Word
{
get
{
_parts.Add( @"(\w+)" );
return this;
}
}
public RegexBuilder groupStart
{
get
{
_parts.Add( "(" );
return this;
}
}
public RegexBuilder groupEnd
{
get
{
_parts.Add( ")" );
return this;
}
}
public RegexBuilder word
{
get
{
_parts.Add( @"\w+" );
return this;
}
}
public RegexBuilder flags( string flags )
{
for ( int i = 0 ; i < flags.Length; i++ )
{
_flags.Add( flags[ i ] + "" );
}
return this;
}
public Regex ToRegex()
{
var options = RegexOptions.None;
var pattern = Lists.Join( _parts, "" );
if ( _flags.IndexOf( "i" ) != -1 )
{
options = options | RegexOptions.IgnoreCase;
}
if ( _flags.IndexOf( "m" ) != -1 )
{
options = options | RegexOptions.Multiline;
}
if ( _flags.IndexOf( "y" ) != -1 )
{
pattern = RegexUtility.MakeSourceSticky( pattern );
}
return new Regex( pattern, options );
}
}
}

View File

@ -0,0 +1,520 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;
using System.Globalization;
using Godot;
namespace Rokojori
{
public class RegexUtility
{
public static Regex MakeSticky( Regex regex )
{
var source = MakeSourceSticky( regex + "" );
return new Regex( source, regex.Options );
}
public static string MakeSourceSticky( string source )
{
if ( ! source.StartsWith( "\\G" ) )
{
source = "\\G(?:" + source + ")";
}
return source;
}
public static string WriteDouble( double value )
{
try
{
var specifier = "G";
return value.ToString( specifier, CultureInfo.InvariantCulture );
}
catch( System.Exception e )
{
RJLog.Log(e );
}
return "0";
}
public static double ParseDouble( string source, double alternative = 0 )
{
try
{
double newValue = System.Double.Parse( source, CultureInfo.InvariantCulture );
return newValue;
}
catch ( System.Exception e )
{
if ( e != null )
{
}
return alternative;
}
}
public static float ParseFloat( string source, float alternative = 0 )
{
return ( float ) ParseDouble( source, alternative );
}
/*public static int ParseInt( string source, int alternative = 0 )
{
return Mathf.RoundToInt( ( float ) ParseDouble( source, alternative ) );
}*/
public static int ParseInt( string source, int alternative = 0 )
{
var multiply = 1;
var value = 0;
for ( int i = source.Length - 1; i >= 0 ; i-- )
{
var symbol = source[ i ];
if ( i == 0 )
{
if ( symbol == '-' )
{ return -value; }
if ( symbol == '+' )
{ return value; }
}
var digitValue = ParseDigit( symbol );
if ( digitValue == -1 )
{
return alternative;
}
value += digitValue * multiply;
multiply *= 10;
}
return value;
}
public static int ParseDigit( char c )
{
switch ( c )
{
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
}
return -1;
}
public static bool Matching( string source, string regex )
{
return new Regex( regex ).Match( source ).Success;
}
public static string Substring( string source, string regex )
{
return new Regex( regex ).Match( source ).Value;
}
public static string EscapeForRegex( string source )
{
var sb = new StringBuilder();
var escapes = "+*?^$\\.[]{}()|/";
for ( int i = 0; i < source.Length; i++ )
{
var character = source[ i ];
if ( escapes.IndexOf( character ) != -1 )
{
sb.Append( "\\" );
}
sb.Append( character );
}
return sb.ToString();
}
public static string LeadingZeros( int value, int minDigits = 2)
{
var stringValue = value + "";
while ( stringValue.Length < minDigits )
{
stringValue = "0" + stringValue;
}
return stringValue;
}
public static string NumberWithThousandsSeperator( int value, string seperator = "." )
{
var isNegative = value < 0;
if ( isNegative ){ value = - value; }
var stringValue = value + "";
var builder = new StringBuilder();
for ( int i = 0; i < stringValue.Length; i++ )
{
var index = ( stringValue.Length - 1 ) - i ;
if ( i % 4 == 3 )
{
builder.Append( seperator );
}
builder.Append( stringValue[ index ] );
}
var seperatorValue = builder.ToString();
return Reverse( seperatorValue );
}
public static string Reverse( string source )
{
char[] characters = source.ToCharArray();
System.Array.Reverse( characters );
return new string( characters );
}
public static Regex EscapedOrRegex( string first, params string[] other )
{
var value = EscapeForRegex( first );
for ( var i = 0; i < other.Length; i++ )
{
value += "|" + EscapeForRegex( other[ i ] );
/*
if ( i == 0 )
{
value = "(" + value;
}
value += ")|(" + EscapeForRegex( other[ i ] );
if ( i == ( other.Length - 1 ) )
{
value += ")";
}
*/
}
//Logs.Log("EscapedOrRegex:", "'" + value + "'");
return new Regex( value );
}
public static Color ParseColor( string source, Color alternative )
{
if ( Matching( source, @"#\d+" ) )
{
return ParseHexColor( source, alternative );
}
else if ( Matching( source, @"^rgb" ) )
{
return ParseRGBColor( source, alternative );
}
else if ( Matching( source, @"^hsl" ) )
{
return ParseHSLColor( source, alternative );
}
return alternative;
}
public static double ParsePercentage( string source )
{
source = Remove( source, @"\s*%" );
return ParseDouble( source.Trim(), 0 ) / 100.0;
}
public static int ParseHexCharacter( char hexCharacter )
{
if ( '0' <= hexCharacter && hexCharacter <= '9' )
{
return (int) (hexCharacter - '0' );
}
if ( 'a' <= hexCharacter && hexCharacter <= 'f' )
{
return (int) (hexCharacter - 'a') + 10;
}
if ( 'A' <= hexCharacter && hexCharacter <= 'F' )
{
return (int) (hexCharacter - 'A') + 10;
}
return 0;
}
public static int ParseHex( string source )
{
var value = 0;
var shift = 0;
for ( var i = source.Length - 1; i < source.Length; i++ )
{
var hexValue = ParseHexCharacter( source[ i ] ) << shift;
shift += 4;
}
return value;
}
public static Color ParseHexColor( string source, Color alternative )
{
source = RegexUtility.Remove( source, @"^#" );
var numbers = new List<float>();
for ( var i = 0; i < 3; i++ )
{
var value = source.Substring( i * 2, 2 );
var numberValue = ParseHex( value ) / 255f;
numbers.Add( numberValue );
}
if ( numbers.Count == 3 )
{
numbers.Add( 1f );
}
return new Color( numbers[ 0 ], numbers[ 1 ], numbers[ 2 ] , numbers[ 3 ] );
}
public static Color ParseRGBColor( string source, Color alternative )
{
source = RegexUtility.Remove( source, @"^rgba?\(" );
source = RegexUtility.Remove( source, @"\)$" );
var splits = Split( source, @"\," );
var numbers = Lists.Map<string,float>( splits,
( string value, int index ) =>
{
if ( value.Contains( "%" ) )
{
return (float) ParsePercentage( value );
}
return ParseFloat( value );
}
);
if ( numbers.Count == 3 )
{
numbers.Add( 1f );
}
return new Color( numbers[ 0 ], numbers[ 1 ], numbers[ 2 ] , numbers[ 3 ] );
}
public static HSLColor ParseHSLColor( string source, HSLColor alternative )
{
source = RegexUtility.Remove( source, @"^hsl\(" );
source = RegexUtility.Remove( source, @"\)$" );
var splits = Split( source, @"\," );
var numbers =Lists.Map<string,float>( splits,
( string value, int index ) =>
{
if ( value.Contains( "%" ) )
{
return (float) ParsePercentage( value );
}
return ParseFloat( value );
}
);
if ( numbers.Count < 3 )
{
RJLog.Log("Not enough numbers parsed: ", source, ">>", numbers.Count, Lists.Join( numbers, "," ) );
}
if ( numbers.Count == 3 )
{
numbers.Add( 1f );
}
return new HSLColor( numbers[ 0 ], numbers[ 1 ], numbers[ 2 ] , numbers[ 3 ] );
}
public static List<string> Split( string source, Regex regex )
{
var strings = regex.Split( source );
var list = new List<string>();
list.AddRange( strings );
return list;
}
public static List<string> Split( string source, string regex )
{
return Split( source, new Regex( regex ) );
}
public static string Remove( string source, string regex )
{
return Replace( source, regex, "" );
}
public static string Replace( string source, Regex regex, string replacement )
{
return regex.Replace( source, replacement );
}
public static string Remove( string source, Regex regex )
{
return Replace( source, regex, "" );
}
public static string Replace( string source, string regex, string replacement )
{
return new Regex( regex ).Replace( source, replacement );
}
public static string ReplaceMultiple( string source, Dictionary<string,string> replacements )
{
var replaced = source;
foreach ( var vk in replacements )
{
replaced = replaced.Replace( vk.Key, vk.Value );
}
return replaced;
}
public static string ParentPathOrLastFragment( string path )
{
var splits = SplitPaths( path );
return splits[ splits.Count - 1 ];
}
public static List<string> SplitPaths( string path )
{
var splittedPaths = new List<string>();
var normalizedPath = NormalizePath( path );
var splits = Split( normalizedPath, new Regex( @"\/" ) );
return splits;
}
public static string JoinPaths( List<string> paths, int startIndex = 0, int length = -1 )
{
var normalizedPaths = new List<string>();
// normalizedPaths.AddRange( paths );
var endIndex = startIndex + length;
var end = ( length < 0 || ( length + startIndex ) >= paths.Count ) ? paths.Count : endIndex;
for ( var i = startIndex; i < end; i++ )
{
normalizedPaths.Add( _NormalizePath( paths[ i ] ) );
}
return Lists.Join( normalizedPaths, "/" );
}
public static string Join( string pathA, string pathB, params string[] paths )
{
var normalizedPaths = new List<string>();
normalizedPaths.Add( pathA );
normalizedPaths.Add( pathB );
normalizedPaths.AddRange( paths );
for ( var i = 0; i < normalizedPaths.Count; i++ )
{
normalizedPaths[ i ] = _NormalizePath( normalizedPaths[ i ] );
}
return Lists.Join( normalizedPaths, "/" );
}
private static string _EnsureForwardSlashes( string path )
{
if ( path.IndexOf( "\\" ) == -1 )
{
return path;
}
var correctedPath = "";
for ( int i = 0; i < path.Length; i++ )
{
if ( path[ i ] == '\\' )
{
correctedPath += "/";
}
else
{
correctedPath += path[ i ];
}
}
return correctedPath;
}
public static string NormalizePath( string path )
{
return _NormalizePath( path );
}
public static string WindowsPath( string path )
{
path = _NormalizePath( path );
var slashes = @"\/";
path = Regex.Replace( path, slashes, "\\" );
return path;
}
private static string _NormalizePath( string path )
{
path = _EnsureForwardSlashes( path );
var startSlashes = @"^\/+";
var endSlashes = @"\/+$";
var multiples = @"\/\/+";
path = Regex.Replace( path, startSlashes, "" );
path = Regex.Replace( path, endSlashes, "" );
path = Regex.Replace( path, multiples, "/" );
return path;
}
}
}

View File

@ -0,0 +1,32 @@
using System.Collections;
using System.Collections.Generic;
namespace Rokojori
{
public class TextAnchor
{
public readonly int lineIndex;
public readonly int characterIndex;
public TextAnchor( int lineIndex, int characterIndex )
{
this.lineIndex = lineIndex;
this.characterIndex = characterIndex;
}
public TextAnchor Copy()
{
return new TextAnchor( lineIndex, characterIndex );
}
public string info
{
get
{
return lineIndex + " (" + characterIndex + ")";
}
}
}
}

75
Runtime/Text/TextLine.cs Normal file
View File

@ -0,0 +1,75 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public class TextLine
{
public readonly int characterIndex;
public readonly int length;
public readonly int lineIndex;
public readonly int lineBreakLength;
public TextLine( int lineIndex, int characterIndex, int length, int lineBreakLength )
{
this.characterIndex = characterIndex;
this.length = length;
this.lineIndex = lineIndex;
this.lineBreakLength = lineBreakLength;
}
public int contentLength { get { return length - lineBreakLength; } }
public int contentOffset { get { return characterIndex + lineBreakLength; } }
public int textEditorLineIndex { get { return lineIndex + 1 ; } }
public string GetContent( string source )
{
return source.Substring( contentOffset, contentLength );
}
public string info
{
get
{
var info = new List<string>();
info.AddRange( new List<string>{
"line index: " + lineIndex,
"character index: " + characterIndex,
"content length: " + contentLength,
"length: " + length,
"line break length: " + lineBreakLength
}
);
return "{ " + Lists.Join( info, ", " ) + " }";
}
}
public TextAnchor GetRawAnchor( int characterIndex )
{
return new TextAnchor( lineIndex, characterIndex - this.characterIndex );
}
public TextAnchor GetTextEditorAnchor( int characterIndex )
{
var lineOffset = this.characterIndex + lineBreakLength;
var offset = Mathf.Max( 0, characterIndex - lineOffset );
return new TextAnchor( lineIndex + 1, offset + 1 );
}
public int lineStart { get { return characterIndex; } }
public int lineEnd { get { return ( characterIndex + length ); } }
public bool Contains( int characterIndex )
{
return lineStart <= characterIndex && characterIndex <= lineEnd;
}
}
}

View File

@ -0,0 +1,233 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Rokojori
{
public class TextLinesMapper
{
List<TextLine> _lines;
int _cachedLineIndex;
void Reset()
{
_lines = new List<TextLine>();
_cachedLineIndex = 0;
}
public void Map( string source )
{
Reset();
var currentCharacterIndex = 0;
currentCharacterIndex = NextLineBreak( source, currentCharacterIndex );
var lastCharacterIndex = 0;
var lastLineBreakLength = 0;
var currentLineIndex = 0;
var maxTries = 10000; var currentTries = 0;
// currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
while ( currentCharacterIndex != -1 )
{
currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
var currentBreakLength = LineBreakLength( source, currentCharacterIndex );
var lineLength = currentCharacterIndex - lastCharacterIndex;
var lineInfo = new TextLine( currentLineIndex, lastCharacterIndex, lineLength, lastLineBreakLength );
_lines.Add( lineInfo );
lastLineBreakLength = currentBreakLength;
lastCharacterIndex = currentCharacterIndex;
currentLineIndex ++;
currentCharacterIndex = NextLineBreak( source, currentCharacterIndex + currentBreakLength );
}
var endLineLength = source.Length - lastCharacterIndex;
var endLineInfo = new TextLine( currentLineIndex, lastCharacterIndex, endLineLength, lastLineBreakLength );
_lines.Add( endLineInfo );
}
public int numCharacters { get { return _lines[ _lines.Count -1 ].lineEnd + 1; } }
public int numLines { get { return _lines.Count; } }
public string lineInfos
{
get
{
var output = new List<string>();
foreach ( var line in _lines )
{
output.Add( line.info );
}
return Lists.Join( output, ",\n" );
}
}
public TextLine GetLine( int characterIndex )
{
var lineIndex = GetLineIndex( characterIndex );
return lineIndex == -1 ? null : _lines[ lineIndex ];
}
public TextAnchor GetAnchor( int characterIndex, bool forTextEditor )
{
var line = GetLine( characterIndex );
if ( line == null ) { return null; }
return forTextEditor ? line.GetTextEditorAnchor( characterIndex ) : line.GetRawAnchor( characterIndex );
}
public TextSelection GetSelection( int startCharacterIndex, int endCharacterIndex, bool forTextEditor = false )
{
var startAnchor = GetAnchor( startCharacterIndex, forTextEditor );
var endAnchor = GetAnchor( endCharacterIndex, forTextEditor );
if ( startAnchor == null || endAnchor == null ) { return null; }
return new TextSelection( startAnchor, endAnchor );
}
public TextSelection GetTextEditorSelection( int startCharacterIndex, int endCharacterIndex )
{
return GetSelection( startCharacterIndex, endCharacterIndex, true );
}
public string GetTextEditorInfo( int startCharacterIndex, int endCharacterIndex )
{
var selection = GetTextEditorSelection( startCharacterIndex, endCharacterIndex );
if ( selection != null ) { return selection.info; }
return "@chars(" + startCharacterIndex + "," + endCharacterIndex + ")";
}
public string GetTextEditorSnippet( string source, int startCharacterIndex, int endCharacterIndex )
{
var startLine = GetLineIndex( startCharacterIndex );
var endLine = GetLineIndex( endCharacterIndex );
if ( endLine < startLine )
{
var b = endLine; endLine = startLine; startLine = b;
}
var snippet = new StringBuilder();
var lengthMaxLineNumberIndex = ( _lines[ endLine ].textEditorLineIndex + "" ).Length;
for ( var i = startLine; i < endLine; i++ )
{
var line = _lines[ i ];
var lineNumber = _lines[ i ].textEditorLineIndex + "";
while ( lineNumber.Length < lengthMaxLineNumberIndex )
{
lineNumber = "0" + lineNumber;
}
var content = line.GetContent( source );
snippet.Append( lineNumber + ": " );
snippet.Append( content );
snippet.Append( "\n" );
}
return snippet.ToString();
}
public string GetTextEditorInfo( int characterIndex )
{
var anchor = GetAnchor( characterIndex, true );
if ( anchor != null ) { return anchor.info; }
return "@chars(" + characterIndex + ")";
}
TextLine _cachedLine { get { return _lines[ _cachedLineIndex ]; } }
public int GetLineIndex( int characterIndex )
{
if ( _cachedLine.Contains( characterIndex ) )
{
return _cachedLineIndex;
}
if ( _lines.Count == 0 || characterIndex < 0 || characterIndex > numCharacters )
{ return -1; }
var lineContainsCharacterIndex = false;
var lineIndexInRange = true;
var characterIndexIsHigher = _cachedLine.characterIndex < characterIndex;
var searchForward = 1;
var searchBackward = -1 ;
var searchStep = characterIndexIsHigher ? searchForward : searchBackward;
var maxTries = 1000; var currentTries = 0;
// currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
do
{
currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
var nextLineIndex = _cachedLineIndex + searchStep;
lineIndexInRange = 0 <= nextLineIndex && nextLineIndex < _lines.Count;
if ( lineIndexInRange )
{
_cachedLineIndex = nextLineIndex;
lineContainsCharacterIndex = _cachedLine.Contains( characterIndex );
}
else
{
lineContainsCharacterIndex = false;
}
}
while ( ! lineContainsCharacterIndex && lineIndexInRange );
if ( lineContainsCharacterIndex )
{
return _cachedLineIndex;
}
return -1;
}
int NextLineBreak( string source, int offset )
{
for ( int i = offset; i < source.Length; i++ )
{
if ( source[ i ] == '\n' || source[ i ] == '\r' )
{
return i;
}
}
return -1;
}
int LineBreakLength( string source, int offset )
{
if ( source[ offset ] == '\r' && ( ( offset + 1 ) < source.Length ) )
{
return source[ offset + 1 ] == '\n' ? 2 : 1;
}
return 1;
}
}
}

View File

@ -0,0 +1,48 @@
using System.Collections;
using System.Collections.Generic;
namespace Rokojori
{
public class TextSelection
{
TextAnchor _start;
TextAnchor _end;
public TextSelection( TextAnchor start, TextAnchor end )
{
_start = start.Copy();
_end = end.Copy();
}
public TextAnchor start { get { return _start; } }
public TextAnchor end { get { return _end; } }
public TextSelection Copy() { return new TextSelection( start, end ); }
public bool isOneLine { get { return _start.lineIndex == _end.lineIndex; } }
public bool isAnchor { get { return isOneLine && _start.characterIndex == _end.characterIndex; } }
public string info
{
get
{
if ( isAnchor )
{
// Output: 12 (1)
return start.info;
}
if ( isOneLine )
{
// Output: 12 (1-6)
return start.lineIndex + " (" + start.characterIndex + "-" + end.characterIndex + ")";
}
// Output: 12-15 (3,12)
return start.lineIndex + "-" + end.lineIndex +
"(" + start.characterIndex + "," + end.characterIndex + ")";
}
}
}
}

250
Runtime/Tools/Lists.cs Normal file
View File

@ -0,0 +1,250 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
namespace Rokojori
{
public class Lists
{
public static List<T> ToList<T>( T[] array )
{
var list = new List<T>();
list.AddRange( array );
return list;
}
public static bool Has<T>( List<T> list, T item )
{
return list.IndexOf( item ) != - 1;
}
public static T Pop<T>( List<T> list )
{
if ( list.Count == 0 ){ return default(T); }
var element = list[ list.Count - 1 ];
list.RemoveAt( list.Count - 1 );
return element;
}
public static T Last<T>( List<T> list )
{
return list.Count == 0 ? default(T) : list[ list.Count - 1 ];
}
public static int CountItems<T>( List<T> list, Predicate<T> test )
{
var result = 0;
for ( int i = 0; i < list.Count; i++ )
{
if ( test( list[ i ] ) )
{
result++;
}
}
return result;
}
public static int CountItems<T>( T[] list, Predicate<T> test )
{
var result = 0;
for ( int i = 0; i < list.Length; i++ )
{
if ( test( list[ i ] ) )
{
result++;
}
}
return result;
}
public static bool AreEntriesEqual<T>( List<T> a, List<T> b )
{
if ( a.Count != b.Count )
{
return false;
}
for ( int i = 0; i < a.Count; i++ )
{
var isEqual = EqualityComparer<T>.Default.Equals( a[ i ], b[ i ]);
if ( ! isEqual )
{
return false;
}
}
return true;
}
public static string Join<T>( List<T> array, string seperator = ", " )
{
var sb = new StringBuilder();
for ( var i = 0; i < array.Count; i++ )
{
if ( i != 0 )
{
sb.Append( seperator );
}
sb.Append( array[ i ] );
}
return sb.ToString();
}
public static List<T> From<T>( T[] array )
{
var copy = new List<T>();
copy.AddRange( array );
return copy;
}
public static List<T> From<T>( List<T> list )
{
var copy = new List<T>();
copy.AddRange( list );
return copy;
}
public static void Filter<T>( List<T> inputList, List<T> list, Func<T,int,bool> filter )
{
for ( int i = 0; i < inputList.Count; i++ )
{
if ( filter( inputList[ i ], i ) )
{
list.Add( inputList[ i ] );
}
}
}
public static void Filter<T>( List<T> inputList, List<T> list, Func<T,bool> filter )
{
for ( int i = 0; i < inputList.Count; i++ )
{
if ( filter( inputList[ i ] ) )
{
list.Add( inputList[ i ] );
}
}
}
public static List<T> Filter<T>( List<T> inputList, Func<T,bool> filter )
{
var list = new List<T>();
for ( int i = 0; i < inputList.Count; i++ )
{
if ( filter( inputList[ i ] ) )
{
list.Add( inputList[ i ] );
}
}
return list;
}
public static List<R> FilterType<T,R>( List<T> inputList ) where R:T
{
var list = new List<R>();
inputList.ForEach
(
e =>
{
if ( e == null || ! typeof( R ).IsAssignableFrom( e.GetType() ) )
{
return;
}
list.Add( (R) e );
}
);
return list;
}
public static T Find<T>( List<T> list, Func<T,bool> callback )
{
for ( int i = 0; i < list.Count; i++ )
{
if ( callback( list[ i ] ) )
{
return list[ i ];
}
}
return default( T );
}
public static List<U> Map<T,U>( List<T> inputList, Func<T,int,U> mapper, List<U> list = null )
{
if ( list == null )
{
list = new List<U>();
}
for ( int i = 0; i < inputList.Count; i++ )
{
list.Add( mapper( inputList[ i ], i ) );
}
return list;
}
public static List<U> Map<T,U>( T[] inputList, Func<T,int,U> mapper, List<U> list = null )
{
if ( list == null )
{
list = new List<U>();
}
for ( int i = 0; i < inputList.Length; i++ )
{
list.Add( mapper( inputList[ i ], i ) );
}
return list;
}
public static List<U> Map<T,U>( List<T> inputList, Func<T,U> mapper, List<U> list = null )
{
if ( list == null )
{
list = new List<U>();
}
for ( int i = 0; i < inputList.Count; i++ )
{
list.Add( mapper( inputList[ i ] ) );
}
return list;
}
public static List<U> Map<T,U>( T[] inputList, Func<T,U> mapper, List<U> list = null )
{
if ( list == null )
{
list = new List<U>();
}
for ( int i = 0; i < inputList.Length; i++ )
{
list.Add( mapper( inputList[ i ] ) );
}
return list;
}
}
}

View File

@ -0,0 +1,241 @@
using System.Collections;
using System.Collections.Generic;
using System;
using System.Reflection;
using System.Text.RegularExpressions;
namespace Rokojori
{
public class ReflectionHelper
{
public static Type GetTypeByNameFromAssembly( string name, Assembly assembly )
{
var type = assembly.GetType( name );
if ( type != null )
{
return type;
}
var types = assembly.GetTypes();
var genericEndingRegex = @"`\d+$";
for ( int i = 0; i < types.Length; i++ )
{
var typeName = types[ i ].Name;
var isGeneric = Regex.IsMatch( typeName, genericEndingRegex );
if ( ! isGeneric )
{
continue;
}
var typeNameWithoutGeneric = Regex.Replace( typeName, genericEndingRegex, "" );
if ( typeNameWithoutGeneric == name )
{
return types[ i ];
}
}
return null;
}
public static Type GetTypeByName( string name)
{
var assemblies = new List<Assembly>();
assemblies.AddRange( AppDomain.CurrentDomain.GetAssemblies() );
assemblies.Reverse();
var assembly = assemblies.Find( a => a.GetType( name ) != null );
return assembly == null ? null : assembly.GetType( name );
}
public static bool IsList( Type type )
{
if ( ! ( type.IsGenericType ) )
{
return false;
}
return type.GetGenericTypeDefinition().IsAssignableFrom( typeof( List<> ) );
}
public static bool IsList( object objectValue )
{
if ( objectValue == null )
{
return false;
}
if ( ! ( objectValue is IList ) )
{
return false;
}
return IsList( objectValue.GetType() );
}
public static bool IsDictionary( Type type )
{
if ( ! ( type.IsGenericType ) )
{
return false;
}
return type.GetGenericTypeDefinition().IsAssignableFrom( typeof( Dictionary<,> ) );
}
public static bool IsDictionary( object objectValue )
{
if ( objectValue == null )
{
return false;
}
if ( ! ( objectValue is IDictionary ) )
{
return false;
}
return IsDictionary( objectValue.GetType() );
}
public static string GetMemberName( object obj, object member )
{
if ( obj == null || member == null )
{
return null;
}
var type = obj.GetType();
var fields = type.GetFields();
for ( int i = 0; i < fields.Length; i++ )
{
var value = fields[ i ].GetValue( obj );
if ( value == member )
{
return fields[ i ].Name;
}
}
return null;
}
public static List<T> GetFieldsOfType<T>( object instance ) where T:class
{
var type = instance.GetType();
var fields = type.GetFields();
var list = new List<T>();
for ( int i = 0; i < fields.Length; i++ )
{
var value = fields[ i ].GetValue( instance );
var tValue = value as T;
if ( tValue != null )
{
list.Add( tValue );
}
}
return list;
}
public static void GetFields<C,T>( System.Action<System.Reflection.FieldInfo> action )
{
GetFields<T>( typeof( C ), action );
}
public static void GetFields<T>( System.Type c, System.Action<System.Reflection.FieldInfo> action )
{
var fields = c.GetFields();
foreach( var f in fields )
{
if ( f.FieldType == typeof( T ) )
{
action( f );
}
}
}
public static System.Reflection.FieldInfo GetFieldInfo( object instance, string memberName )
{
var type = instance.GetType();
var fields = type.GetFields();
var index = -1;
for ( int i = 0; i < fields.Length; i++ )
{
if ( fields[ i ].Name == memberName )
{
index = i;
}
}
return index == -1 ? null : fields[ index ];
}
public static bool HasMember( object instance, string memberName )
{
return GetFieldInfo( instance, memberName ) != null;
}
public static object GetMemberValue( object instance, string memberName )
{
try
{
var field = ReflectionHelper.GetFieldInfo( instance, memberName );
if ( field == null )
{
RJLog.Log(instance, "GetValue: member not found with name ", "'" + memberName + "'" );
return null;
}
return field.GetValue( instance );
}
catch( System.Exception e )
{
RJLog.Log(e );
}
return null;
}
public static void SetMemberValue( object instance, string memberName, object value )
{
try
{
var field = ReflectionHelper.GetFieldInfo( instance, memberName );
if ( field == null )
{
RJLog.Log(instance, "SetValue: member not found with name ", "'" + memberName + "'" );
return;
}
field.SetValue( instance, value );
}
catch( System.Exception e )
{
RJLog.Log(e );
}
}
}
}