Skip to content

Instantly share code, notes, and snippets.

@Gargaj
Last active July 4, 2020 22:11
Show Gist options
  • Save Gargaj/4a70352921b0ed9dd3730dac4d401ce8 to your computer and use it in GitHub Desktop.
Save Gargaj/4a70352921b0ed9dd3730dac4d401ce8 to your computer and use it in GitHub Desktop.
/*
Uses https://github.com/datalust/superpower
Usage:
var text = System.IO.File.ReadAllText("visuals.comp");
var parsed = FusionCompParser.ParseComp(text);
var tools = parsed.Get<FusionCompParser.FusionArray>("Tools");
*/
using Superpower;
using Superpower.Display;
using Superpower.Model;
using Superpower.Parsers;
using Superpower.Tokenizers;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
namespace FusionCompParser
{
public class FusionCompParser
{
public class FusionArray
{
public string Type { get; set; }
public string Modifier { get; set; }
public List<object> Array { get; set; }
public bool Contains(string key)
{
return Array
?.Where(s => s is KeyValuePair<string, object>)
?.Select(s => (KeyValuePair<string, object>)s)
.Any(s => s.Key == key) ?? false;
}
public T Get<T>(string key)
{
return (T)Array
?.Where(s => s is KeyValuePair<string, object>)
?.Select(s => (KeyValuePair<string, object>)s)
.FirstOrDefault(s => s.Key == key)
.Value;
}
public T GetOrDefault<T>(T defaultValue, string key)
{
var l = Array
.Where(s => s is KeyValuePair<string, object>)
.Select(s => (KeyValuePair<string, object>)s);
if (!l.Any(s => s.Key == key))
{
return defaultValue;
}
return (T)l.FirstOrDefault(s => s.Key == key).Value;
}
public bool Contains(params string[] keys)
{
var array = this;
for (int i = 0; i < keys.Length - 1; i++)
{
var key = keys[i];
array = array.Get<FusionArray>(key);
if (array == null)
{
return false;
}
}
return array.Contains(keys[keys.Length - 1]);
}
public T Get<T>(params string[] keys)
{
var array = this;
for (int i = 0; i < keys.Length - 1; i++)
{
var key = keys[i];
array = array.Get<FusionArray>(key);
if (array == null)
{
return default(T);
}
}
return array.Get<T>(keys[keys.Length - 1]);
}
public T GetOrDefault<T>(T defaultValue, params string[] keys)
{
var array = this;
for (int i = 0; i < keys.Length - 1; i++)
{
var key = keys[i];
array = array.Get<FusionArray>(key);
if (array == null)
{
return defaultValue;
}
}
return array.GetOrDefault<T>(defaultValue, keys[keys.Length - 1]);
}
}
enum FusionToken
{
[Token(Example = "{")]
LBracket,
[Token(Example = "}")]
RBracket,
[Token(Example = "}")]
RBracketArrayTerminator,
[Token(Example = "=")]
Equals,
[Token(Example = ",")]
Comma,
Symbol,
String,
Number,
Literal,
Modifier,
Identifier,
Terminator,
}
static class FusionTokenizer
{
static TextParser<Unit> FusionStringToken { get; } =
from open in Character.EqualTo('"')
from content in Span.EqualTo("\\\"").Value(Unit.Value).Try()
.Or(Character.Except('"').Value(Unit.Value))
.IgnoreMany()
from close in Character.EqualTo('"')
select Unit.Value;
static TextParser<Unit> FusionLiteral { get; } =
from open1 in Character.EqualTo('[').OptionalOrDefault()
from open2 in Character.EqualTo('"').OptionalOrDefault()
from content in Character.LetterOrDigit.Or(Character.In('.', '-', '_')).IgnoreMany()
from close2 in Character.EqualTo('"').OptionalOrDefault()
from close1 in Character.EqualTo(']').OptionalOrDefault()
select Unit.Value;
static TextParser<Unit> FusionArrayTerminator { get; } =
from open1 in Character.EqualTo(',').OptionalOrDefault()
from _ in Span.WhiteSpace.Many().OptionalOrDefault()
from open2 in Character.EqualTo('}')
select Unit.Value;
static TextParser<Unit> FusionNumberToken { get; } =
from sign in Character.EqualTo('-').OptionalOrDefault()
from first in Character.Digit
from rest in Character.Digit.Or(Character.In('.', 'e', 'E', '+', '-')).IgnoreMany()
select Unit.Value;
static TextParser<Unit> FusionModifier { get; } =
from keyword in Character.Lower.IgnoreMany()
from parentheses1 in Character.EqualTo('(')
from parentheses2 in Character.EqualTo(')')
select Unit.Value;
public static Tokenizer<FusionToken> Instance { get; } =
new TokenizerBuilder<FusionToken>()
.Ignore(Span.WhiteSpace)
.Match(FusionArrayTerminator, FusionToken.RBracketArrayTerminator)
.Match(Character.EqualTo('{'), FusionToken.LBracket)
.Match(Character.EqualTo('}'), FusionToken.RBracket)
.Match(Character.EqualTo('='), FusionToken.Equals)
.Match(Character.EqualTo(','), FusionToken.Comma)
.Match(Character.EqualTo('\0'), FusionToken.Terminator)
.Match(FusionStringToken, FusionToken.String)
.Match(FusionNumberToken, FusionToken.Number, requireDelimiters: true)
.Match(FusionModifier, FusionToken.Modifier)
.Match(FusionLiteral, FusionToken.Literal)
.Build();
}
static class FusionTextParsers
{
public static TextParser<string> String { get; } =
from open in Character.EqualTo('"')
from chars in Character.ExceptIn('"', '\\')
.Or(Character.EqualTo('\\')
.IgnoreThen(
Character.EqualTo('\\')
.Or(Character.EqualTo('"'))
.Or(Character.EqualTo('/'))
.Or(Character.EqualTo('b').Value('\b'))
.Or(Character.EqualTo('f').Value('\f'))
.Or(Character.EqualTo('n').Value('\n'))
.Or(Character.EqualTo('r').Value('\r'))
.Or(Character.EqualTo('t').Value('\t'))
.Or(Character.EqualTo('u').IgnoreThen(
Span.MatchedBy(Character.HexDigit.Repeat(4))
.Apply(Numerics.HexDigitsUInt32)
.Select(cc => (char)cc)))
.Named("escape sequence")))
.Many()
from close in Character.EqualTo('"')
select new string(chars);
public static TextParser<double> Number { get; } =
from sign in Character.EqualTo('-').Value(-1.0).OptionalOrDefault(1.0)
from whole in Numerics.Natural.Select(n => double.Parse(n.ToStringValue()))
from frac in Character.EqualTo('.')
.IgnoreThen(Numerics.Natural)
.Select(n => double.Parse(n.ToStringValue()) * Math.Pow(10, -n.Length))
.OptionalOrDefault()
from exp in Character.EqualToIgnoreCase('e')
.IgnoreThen(Character.EqualTo('+').Value(1.0)
.Or(Character.EqualTo('-').Value(-1.0))
.OptionalOrDefault(1.0))
.Then(expsign => Numerics.Natural.Select(n => double.Parse(n.ToStringValue()) * expsign))
.OptionalOrDefault()
select (whole + frac) * sign * Math.Pow(10, exp);
public static TextParser<string> Literal { get; } =
from open1 in Character.EqualTo('[').Optional()
from open2 in Character.EqualTo('"').Optional()
from chars in Character.ExceptIn('"', '\\')
.Or(Character.EqualTo('\\')
.IgnoreThen(
Character.EqualTo('\\')
.Or(Character.EqualTo('"'))
.Or(Character.EqualTo('/'))
.Or(Character.EqualTo('b').Value('\b'))
.Or(Character.EqualTo('f').Value('\f'))
.Or(Character.EqualTo('n').Value('\n'))
.Or(Character.EqualTo('r').Value('\r'))
.Or(Character.EqualTo('t').Value('\t'))
.Or(Character.EqualTo('u').IgnoreThen(
Span.MatchedBy(Character.HexDigit.Repeat(4))
.Apply(Numerics.HexDigitsUInt32)
.Select(cc => (char)cc)))
.Named("escape sequence")))
.Many()
from close2 in Character.EqualTo('"').Optional()
from close1 in Character.EqualTo(']').Optional()
select new string(chars);
}
private static TokenListParser<FusionToken, object> FusionParserString { get; } =
Token.EqualTo(FusionToken.String)
.Apply(FusionTextParsers.String)
.Select(s => (object)s);
private static TokenListParser<FusionToken, object> FusionParserNumber { get; } =
Token.EqualTo(FusionToken.Number)
.Apply(FusionTextParsers.Number)
.Select(n => (object)n);
private static TokenListParser<FusionToken, object> FusionParserKeyValuePair { get; } =
from key in Token.EqualTo(FusionToken.Literal).Try()
.Named("kvp key")
.Apply(FusionTextParsers.Literal)
.Select(s => s)
from eq in Token.EqualTo(FusionToken.Equals).Try()
from value in Parse.Ref(() => FusionParserValue)
.Named("kvp value")
select (object)KeyValuePair.Create(key, value);
private static TokenListParser<FusionToken, object> FusionParserArray { get; } =
from type in Token.EqualTo(FusionToken.Literal)
.Named("array/object type")
.Apply(FusionTextParsers.Literal)
.Select(s => s)
.OptionalOrDefault("")
from modifier in Token.EqualTo(FusionToken.Modifier)
.Named("modifier")
.Select(s => s.ToString())
.OptionalOrDefault("")
from begin in Token.EqualTo(FusionToken.LBracket)
from properties in FusionParserValue
.Select(value => value)
.ManyDelimitedBy(
Token.EqualTo(FusionToken.Comma),
Token.EqualTo(FusionToken.RBracketArrayTerminator)
)
select (object)new FusionArray() { Type = type, Modifier = modifier, Array = properties.ToList() };
private static TokenListParser<FusionToken, object> FusionParserTrue { get; } =
Token.EqualToValue(FusionToken.Literal, "true").Value((object)true);
private static TokenListParser<FusionToken, object> FusionParserFalse { get; } =
Token.EqualToValue(FusionToken.Literal, "false").Value((object)false);
private static TokenListParser<FusionToken, object> FusionParserValue { get; } =
FusionParserString
.Or(FusionParserNumber)
.Or(FusionParserTrue)
.Or(FusionParserFalse)
.Or(FusionParserKeyValuePair)
.Or(FusionParserArray)
.Named("Fusion value");
private static TokenListParser<FusionToken, object> FusionParserDocument { get; } =
from docu in FusionParserValue.Select(s => s)
from term in Token.EqualTo(FusionToken.Terminator).Optional()
select docu;
public static FusionArray ParseComp(string data)
{
var tokens = FusionTokenizer.Instance.TryTokenize(data);
if (!tokens.HasValue)
{
return null;
}
var parsed = FusionParserDocument.TryParse(tokens.Value);
if (!parsed.HasValue)
{
return null;
}
return parsed.Value as FusionArray;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment