Skip to content

Instantly share code, notes, and snippets.

@rogeralsing
Last active February 29, 2016 11:33
Show Gist options
  • Save rogeralsing/8684159 to your computer and use it in GitHub Desktop.
Save rogeralsing/8684159 to your computer and use it in GitHub Desktop.
Akka config parser
using System;
using System.Collections.Generic;
using System.Text;
namespace ConfigParser
{
internal class Program
{
private static void Main(string[] args)
{
string data = @"
####################################
# Akka Actor Reference Config File #
####################################
# This is the reference config file that contains all the default settings.
# Make your edits/overrides in your application.conf.
akka {
# Akka version, checked against the runtime version of Akka.
version = ""2.3-SNAPSHOT""
# Home directory of Akka, modules in the deploy directory will be loaded
home = """"
# Loggers to register at boot time (akka.event.Logging$DefaultLogger logs
# to STDOUT)
loggers = [""akka.event.Logging$DefaultLogger""]
# Loggers are created and registered synchronously during ActorSystem
# start-up, and since they are actors, this timeout is used to bound the
# waiting time
logger-startup-timeout = 5s
# Log level used by the configured loggers (see ""loggers"") as soon
# as they have been started; before that, see ""stdout-loglevel""
# Options: OFF, ERROR, WARNING, INFO, DEBUG
loglevel = ""INFO""
# Log level for the very basic logger activated during AkkaApplication startup
# Options: OFF, ERROR, WARNING, INFO, DEBUG
stdout-loglevel = ""WARNING""
# Log the complete configuration at INFO level when the actor system is started.
# This is useful when you are uncertain of what configuration is used.
log-config-on-start = off
# Log at info level when messages are sent to dead letters.
# Possible values:
# on: all dead letters are logged
# off: no logging of dead letters
# n: positive integer, number of dead letters that will be logged
log-dead-letters = 10
# Possibility to turn off logging of dead letters while the actor system
# is shutting down. Logging is only done when enabled by 'log-dead-letters'
# setting.
log-dead-letters-during-shutdown = on
# List FQCN of extensions which shall be loaded at actor system startup.
# Should be on the format: 'extensions = [""foo"", ""bar""]' etc.
# See the Akka Documentation for more info about Extensions
extensions = []
# Toggles whether threads created by this ActorSystem should be daemons or not
daemonic = off
# JVM shutdown, System.exit(-1), in case of a fatal error,
# such as OutOfMemoryError
jvm-exit-on-fatal-error = on
actor {
# FQCN of the ActorRefProvider to be used; the below is the built-in default,
# another one is akka.remote.RemoteActorRefProvider in the akka-remote bundle.
provider = ""akka.actor.LocalActorRefProvider""
# The guardian ""/user"" will use this class to obtain its supervisorStrategy.
# It needs to be a subclass of akka.actor.SupervisorStrategyConfigurator.
# In addition to the default there is akka.actor.StoppingSupervisorStrategy.
guardian-supervisor-strategy = ""akka.actor.DefaultSupervisorStrategy""
# Timeout for ActorSystem.actorOf
creation-timeout = 20s
# Frequency with which stopping actors are prodded in case they had to be
# removed from their parents
reaper-interval = 5s
# Serializes and deserializes (non-primitive) messages to ensure immutability,
# this is only intended for testing.
serialize-messages = off
# Serializes and deserializes creators (in Props) to ensure that they can be
# sent over the network, this is only intended for testing. Purely local deployments
# as marked with deploy.scope == LocalScope are exempt from verification.
serialize-creators = off
# Timeout for send operations to top-level actors which are in the process
# of being started. This is only relevant if using a bounded mailbox or the
# CallingThreadDispatcher for a top-level actor.
unstarted-push-timeout = 10s
typed {
# Default timeout for typed actor methods with non-void return type
timeout = 5s
}
# Mapping between ´deployment.router' short names to fully qualified class names
router.type-mapping {
from-code = ""akka.routing.NoRouter""
round-robin-pool = ""akka.routing.RoundRobinPool""
round-robin-group = ""akka.routing.RoundRobinGroup""
random-pool = ""akka.routing.RandomPool""
random-group = ""akka.routing.RandomGroup""
balancing-pool = ""akka.routing.BalancingPool""
smallest-mailbox-pool = ""akka.routing.SmallestMailboxPool""
broadcast-pool = ""akka.routing.BroadcastPool""
broadcast-group = ""akka.routing.BroadcastGroup""
scatter-gather-pool = ""akka.routing.ScatterGatherFirstCompletedPool""
scatter-gather-group = ""akka.routing.ScatterGatherFirstCompletedGroup""
consistent-hashing-pool = ""akka.routing.ConsistentHashingPool""
consistent-hashing-group = ""akka.routing.ConsistentHashingGroup""
}
deployment {
# deployment id pattern - on the format: /parent/child etc.
default {
# The id of the dispatcher to use for this actor.
# If undefined or empty the dispatcher specified in code
# (Props.withDispatcher) is used, or default-dispatcher if not
# specified at all.
dispatcher = """"
# The id of the mailbox to use for this actor.
# If undefined or empty the default mailbox of the configured dispatcher
# is used or if there is no mailbox configuration the mailbox specified
# in code (Props.withMailbox) is used.
# If there is a mailbox defined in the configured dispatcher then that
# overrides this setting.
mailbox = """"
# routing (load-balance) scheme to use
# - available: ""from-code"", ""round-robin"", ""random"", ""smallest-mailbox"",
# ""scatter-gather"", ""broadcast""
# - or: Fully qualified class name of the router class.
# The class must extend akka.routing.CustomRouterConfig and
# have a public constructor with com.typesafe.config.Config
# and optional akka.actor.DynamicAccess parameter.
# - default is ""from-code"";
# Whether or not an actor is transformed to a Router is decided in code
# only (Props.withRouter). The type of router can be overridden in the
# configuration; specifying ""from-code"" means that the values specified
# in the code shall be used.
# In case of routing, the actors to be routed to can be specified
# in several ways:
# - nr-of-instances: will create that many children
# - routees.paths: will route messages to these paths using ActorSelection,
# i.e. will not create children
# - resizer: dynamically resizable number of routees as specified in
# resizer below
router = ""from-code""
# number of children to create in case of a router;
# this setting is ignored if routees.paths is given
nr-of-instances = 1
# within is the timeout used for routers containing future calls
within = 5 seconds
# number of virtual nodes per node for consistent-hashing router
virtual-nodes-factor = 10
routees {
# Alternatively to giving nr-of-instances you can specify the full
# paths of those actors which should be routed to. This setting takes
# precedence over nr-of-instances
paths = []
}
# To use a dedicated dispatcher for the routees of the pool you can
# define the dispatcher configuration inline with the property name
# 'pool-dispatcher' in the deployment section of the router.
# For example:
# pool-dispatcher {
# fork-join-executor.parallelism-min = 5
# fork-join-executor.parallelism-max = 5
# }
# Routers with dynamically resizable number of routees; this feature is
# enabled by including (parts of) this section in the deployment
resizer {
enabled = off
# The fewest number of routees the router should ever have.
lower-bound = 1
# The most number of routees the router should ever have.
# Must be greater than or equal to lower-bound.
upper-bound = 10
# Threshold used to evaluate if a routee is considered to be busy
# (under pressure). Implementation depends on this value (default is 1).
# 0: number of routees currently processing a message.
# 1: number of routees currently processing a message has
# some messages in mailbox.
# > 1: number of routees with at least the configured pressure-threshold
# messages in their mailbox. Note that estimating mailbox size of
# default UnboundedMailbox is O(N) operation.
pressure-threshold = 1
# Percentage to increase capacity whenever all routees are busy.
# For example, 0.2 would increase 20% (rounded up), i.e. if current
# capacity is 6 it will request an increase of 2 more routees.
rampup-rate = 0.2
# Minimum fraction of busy routees before backing off.
# For example, if this is 0.3, then we'll remove some routees only when
# less than 30% of routees are busy, i.e. if current capacity is 10 and
# 3 are busy then the capacity is unchanged, but if 2 or less are busy
# the capacity is decreased.
# Use 0.0 or negative to avoid removal of routees.
backoff-threshold = 0.3
# Fraction of routees to be removed when the resizer reaches the
# backoffThreshold.
# For example, 0.1 would decrease 10% (rounded up), i.e. if current
# capacity is 9 it will request an decrease of 1 routee.
backoff-rate = 0.1
# Number of messages between resize operation.
# Use 1 to resize before each message.
messages-per-resize = 10
}
}
}
default-dispatcher {
# Must be one of the following
# Dispatcher, PinnedDispatcher, or a FQCN to a class inheriting
# MessageDispatcherConfigurator with a public constructor with
# both com.typesafe.config.Config parameter and
# akka.dispatch.DispatcherPrerequisites parameters.
# PinnedDispatcher must be used together with executor=thread-pool-executor.
type = ""Dispatcher""
# Which kind of ExecutorService to use for this dispatcher
# Valid options:
# - ""default-executor"" requires a ""default-executor"" section
# - ""fork-join-executor"" requires a ""fork-join-executor"" section
# - ""thread-pool-executor"" requires a ""thread-pool-executor"" section
# - A FQCN of a class extending ExecutorServiceConfigurator
executor = ""default-executor""
# This will be used if you have set ""executor = ""default-executor"""".
# If an ActorSystem is created with a given ExecutionContext, this
# ExecutionContext will be used as the default executor for all
# dispatchers in the ActorSystem configured with
# executor = ""default-executor"". Note that ""default-executor""
# is the default value for executor, and therefore used if not
# specified otherwise. If no ExecutionContext is given,
# the executor configured in ""fallback"" will be used.
default-executor {
fallback = ""fork-join-executor""
}
# This will be used if you have set ""executor = ""fork-join-executor""""
fork-join-executor {
# Min number of threads to cap factor-based parallelism number to
parallelism-min = 8
# The parallelism factor is used to determine thread pool size using the
# following formula: ceil(available processors * factor). Resulting size
# is then bounded by the parallelism-min and parallelism-max values.
parallelism-factor = 3.0
# Max number of threads to cap factor-based parallelism number to
parallelism-max = 64
}
# This will be used if you have set ""executor = ""thread-pool-executor""""
thread-pool-executor {
# Keep alive time for threads
keep-alive-time = 60s
# Min number of threads to cap factor-based core number to
core-pool-size-min = 8
# The core pool size factor is used to determine thread pool core size
# using the following formula: ceil(available processors * factor).
# Resulting size is then bounded by the core-pool-size-min and
# core-pool-size-max values.
core-pool-size-factor = 3.0
# Max number of threads to cap factor-based number to
core-pool-size-max = 64
# Minimum number of threads to cap factor-based max number to
# (if using a bounded task queue)
max-pool-size-min = 8
# Max no of threads (if using a bounded task queue) is determined by
# calculating: ceil(available processors * factor)
max-pool-size-factor = 3.0
# Max number of threads to cap factor-based max number to
# (if using a bounded task queue)
max-pool-size-max = 64
# Specifies the bounded capacity of the task queue (< 1 == unbounded)
task-queue-size = -1
# Specifies which type of task queue will be used, can be ""array"" or
# ""linked"" (default)
task-queue-type = ""linked""
# Allow core threads to time out
allow-core-timeout = on
}
# How long time the dispatcher will wait for new actors until it shuts down
shutdown-timeout = 1s
# Throughput defines the number of messages that are processed in a batch
# before the thread is returned to the pool. Set to 1 for as fair as possible.
throughput = 5
# Throughput deadline for Dispatcher, set to 0 or negative for no deadline
throughput-deadline-time = 0ms
# For BalancingDispatcher: If the balancing dispatcher should attempt to
# schedule idle actors using the same dispatcher when a message comes in,
# and the dispatchers ExecutorService is not fully busy already.
attempt-teamwork = on
# If this dispatcher requires a specific type of mailbox, specify the
# fully-qualified class name here; the actually created mailbox will
# be a subtype of this type. The empty string signifies no requirement.
mailbox-requirement = """"
}
default-mailbox {
# FQCN of the MailboxType. The Class of the FQCN must have a public
# constructor with
# (akka.actor.ActorSystem.Settings, com.typesafe.config.Config) parameters.
mailbox-type = ""akka.dispatch.UnboundedMailbox""
# If the mailbox is bounded then it uses this setting to determine its
# capacity. The provided value must be positive.
# NOTICE:
# Up to version 2.1 the mailbox type was determined based on this setting;
# this is no longer the case, the type must explicitly be a bounded mailbox.
mailbox-capacity = 1000
# If the mailbox is bounded then this is the timeout for enqueueing
# in case the mailbox is full. Negative values signify infinite
# timeout, which should be avoided as it bears the risk of dead-lock.
mailbox-push-timeout-time = 10s
# For Actor with Stash: The default capacity of the stash.
# If negative (or zero) then an unbounded stash is used (default)
# If positive then a bounded stash is used and the capacity is set using
# the property
stash-capacity = -1
}
mailbox {
# Mapping between message queue semantics and mailbox configurations.
# Used by akka.dispatch.RequiresMessageQueue[T] to enforce different
# mailbox types on actors.
# If your Actor implements RequiresMessageQueue[T], then when you create
# an instance of that actor its mailbox type will be decided by looking
# up a mailbox configuration via T in this mapping
requirements {
""akka.dispatch.UnboundedMessageQueueSemantics"" = akka.actor.mailbox.unbounded-queue-based
""akka.dispatch.BoundedMessageQueueSemantics"" = akka.actor.mailbox.bounded-queue-based
""akka.dispatch.DequeBasedMessageQueueSemantics"" = akka.actor.mailbox.unbounded-deque-based
""akka.dispatch.UnboundedDequeBasedMessageQueueSemantics"" = akka.actor.mailbox.unbounded-deque-based
""akka.dispatch.BoundedDequeBasedMessageQueueSemantics"" = akka.actor.mailbox.bounded-deque-based
""akka.dispatch.MultipleConsumerSemantics"" = akka.actor.mailbox.unbounded-queue-based
}
unbounded-queue-based {
# FQCN of the MailboxType, The Class of the FQCN must have a public
# constructor with (akka.actor.ActorSystem.Settings,
# com.typesafe.config.Config) parameters.
mailbox-type = ""akka.dispatch.UnboundedMailbox""
}
bounded-queue-based {
# FQCN of the MailboxType, The Class of the FQCN must have a public
# constructor with (akka.actor.ActorSystem.Settings,
# com.typesafe.config.Config) parameters.
mailbox-type = ""akka.dispatch.BoundedMailbox""
}
unbounded-deque-based {
# FQCN of the MailboxType, The Class of the FQCN must have a public
# constructor with (akka.actor.ActorSystem.Settings,
# com.typesafe.config.Config) parameters.
mailbox-type = ""akka.dispatch.UnboundedDequeBasedMailbox""
}
bounded-deque-based {
# FQCN of the MailboxType, The Class of the FQCN must have a public
# constructor with (akka.actor.ActorSystem.Settings,
# com.typesafe.config.Config) parameters.
mailbox-type = ""akka.dispatch.BoundedDequeBasedMailbox""
}
}
debug {
# enable function of Actor.loggable(), which is to log any received message
# at DEBUG level, see the “Testing Actor Systems” section of the Akka
# Documentation at http://akka.io/docs
receive = off
# enable DEBUG logging of all AutoReceiveMessages (Kill, PoisonPill et.c.)
autoreceive = off
# enable DEBUG logging of actor lifecycle changes
lifecycle = off
# enable DEBUG logging of all LoggingFSMs for events, transitions and timers
fsm = off
# enable DEBUG logging of subscription changes on the eventStream
event-stream = off
# enable DEBUG logging of unhandled messages
unhandled = off
# enable WARN logging of misconfigured routers
router-misconfiguration = off
}
# Entries for pluggable serializers and their bindings.
serializers {
java = ""akka.serialization.JavaSerializer""
bytes = ""akka.serialization.ByteArraySerializer""
}
# Class to Serializer binding. You only need to specify the name of an
# interface or abstract base class of the messages. In case of ambiguity it
# is using the most specific configured class, or giving a warning and
# choosing the “first” one.
#
# To disable one of the default serializers, assign its class to ""none"", like
# ""java.io.Serializable"" = none
serialization-bindings {
""[B"" = bytes
""java.io.Serializable"" = java
}
# Configuration items which are used by the akka.actor.ActorDSL._ methods
dsl {
# Maximum queue size of the actor created by newInbox(); this protects
# against faulty programs which use select() and consistently miss messages
inbox-size = 1000
# Default timeout to assume for operations like Inbox.receive et al
default-timeout = 5s
}
}
# Used to set the behavior of the scheduler.
# Changing the default values may change the system behavior drastically so make
# sure you know what you're doing! See the Scheduler section of the Akka
# Documentation for more details.
scheduler {
# The LightArrayRevolverScheduler is used as the default scheduler in the
# system. It does not execute the scheduled tasks on exact time, but on every
# tick, it will run everything that is (over)due. You can increase or decrease
# the accuracy of the execution timing by specifying smaller or larger tick
# duration. If you are scheduling a lot of tasks you should consider increasing
# the ticks per wheel.
# Note that it might take up to 1 tick to stop the Timer, so setting the
# tick-duration to a high value will make shutting down the actor system
# take longer.
tick-duration = 10ms
# The timer uses a circular wheel of buckets to store the timer tasks.
# This should be set such that the majority of scheduled timeouts (for high
# scheduling frequency) will be shorter than one rotation of the wheel
# (ticks-per-wheel * ticks-duration)
# THIS MUST BE A POWER OF TWO!
ticks-per-wheel = 512
# This setting selects the timer implementation which shall be loaded at
# system start-up.
# The class given here must implement the akka.actor.Scheduler interface
# and offer a public constructor which takes three arguments:
# 1) com.typesafe.config.Config
# 2) akka.event.LoggingAdapter
# 3) java.util.concurrent.ThreadFactory
implementation = akka.actor.LightArrayRevolverScheduler
# When shutting down the scheduler, there will typically be a thread which
# needs to be stopped, and this timeout determines how long to wait for
# that to happen. In case of timeout the shutdown of the actor system will
# proceed without running possibly still enqueued tasks.
shutdown-timeout = 5s
}
io {
# By default the select loops run on dedicated threads, hence using a
# PinnedDispatcher
pinned-dispatcher {
type = ""PinnedDispatcher""
executor = ""thread-pool-executor""
thread-pool-executor.allow-core-pool-timeout = off
}
tcp {
# The number of selectors to stripe the served channels over; each of
# these will use one select loop on the selector-dispatcher.
nr-of-selectors = 1
# Maximum number of open channels supported by this TCP module; there is
# no intrinsic general limit, this setting is meant to enable DoS
# protection by limiting the number of concurrently connected clients.
# Also note that this is a ""soft"" limit; in certain cases the implementation
# will accept a few connections more or a few less than the number configured
# here. Must be an integer > 0 or ""unlimited"".
max-channels = 256000
# When trying to assign a new connection to a selector and the chosen
# selector is at full capacity, retry selector choosing and assignment
# this many times before giving up
selector-association-retries = 10
# The maximum number of connection that are accepted in one go,
# higher numbers decrease latency, lower numbers increase fairness on
# the worker-dispatcher
batch-accept-limit = 10
# The number of bytes per direct buffer in the pool used to read or write
# network data from the kernel.
direct-buffer-size = 128 KiB
# The maximal number of direct buffers kept in the direct buffer pool for
# reuse.
direct-buffer-pool-limit = 1000
# The duration a connection actor waits for a `Register` message from
# its commander before aborting the connection.
register-timeout = 5s
# The maximum number of bytes delivered by a `Received` message. Before
# more data is read from the network the connection actor will try to
# do other work.
max-received-message-size = unlimited
# Enable fine grained logging of what goes on inside the implementation.
# Be aware that this may log more than once per message sent to the actors
# of the tcp implementation.
trace-logging = off
# Fully qualified config path which holds the dispatcher configuration
# to be used for running the select() calls in the selectors
selector-dispatcher = ""akka.io.pinned-dispatcher""
# Fully qualified config path which holds the dispatcher configuration
# for the read/write worker actors
worker-dispatcher = ""akka.actor.default-dispatcher""
# Fully qualified config path which holds the dispatcher configuration
# for the selector management actors
management-dispatcher = ""akka.actor.default-dispatcher""
# Fully qualified config path which holds the dispatcher configuration
# on which file IO tasks are scheduled
file-io-dispatcher = ""akka.actor.default-dispatcher""
# The maximum number of bytes (or ""unlimited"") to transfer in one batch
# when using `WriteFile` command which uses `FileChannel.transferTo` to
# pipe files to a TCP socket. On some OS like Linux `FileChannel.transferTo`
# may block for a long time when network IO is faster than file IO.
# Decreasing the value may improve fairness while increasing may improve
# throughput.
file-io-transferTo-limit = 512 KiB
# The number of times to retry the `finishConnect` call after being notified about
# OP_CONNECT. Retries are needed if the OP_CONNECT notification doesn't imply that
# `finishConnect` will succeed, which is the case on Android.
finish-connect-retries = 5
}
udp {
# The number of selectors to stripe the served channels over; each of
# these will use one select loop on the selector-dispatcher.
nr-of-selectors = 1
# Maximum number of open channels supported by this UDP module Generally
# UDP does not require a large number of channels, therefore it is
# recommended to keep this setting low.
max-channels = 4096
# The select loop can be used in two modes:
# - setting ""infinite"" will select without a timeout, hogging a thread
# - setting a positive timeout will do a bounded select call,
# enabling sharing of a single thread between multiple selectors
# (in this case you will have to use a different configuration for the
# selector-dispatcher, e.g. using ""type=Dispatcher"" with size 1)
# - setting it to zero means polling, i.e. calling selectNow()
select-timeout = infinite
# When trying to assign a new connection to a selector and the chosen
# selector is at full capacity, retry selector choosing and assignment
# this many times before giving up
selector-association-retries = 10
# The maximum number of datagrams that are read in one go,
# higher numbers decrease latency, lower numbers increase fairness on
# the worker-dispatcher
receive-throughput = 3
# The number of bytes per direct buffer in the pool used to read or write
# network data from the kernel.
direct-buffer-size = 128 KiB
# The maximal number of direct buffers kept in the direct buffer pool for
# reuse.
direct-buffer-pool-limit = 1000
# The maximum number of bytes delivered by a `Received` message. Before
# more data is read from the network the connection actor will try to
# do other work.
received-message-size-limit = unlimited
# Enable fine grained logging of what goes on inside the implementation.
# Be aware that this may log more than once per message sent to the actors
# of the tcp implementation.
trace-logging = off
# Fully qualified config path which holds the dispatcher configuration
# to be used for running the select() calls in the selectors
selector-dispatcher = ""akka.io.pinned-dispatcher""
# Fully qualified config path which holds the dispatcher configuration
# for the read/write worker actors
worker-dispatcher = ""akka.actor.default-dispatcher""
# Fully qualified config path which holds the dispatcher configuration
# for the selector management actors
management-dispatcher = ""akka.actor.default-dispatcher""
}
udp-connected {
# The number of selectors to stripe the served channels over; each of
# these will use one select loop on the selector-dispatcher.
nr-of-selectors = 1
# Maximum number of open channels supported by this UDP module Generally
# UDP does not require a large number of channels, therefore it is
# recommended to keep this setting low.
max-channels = 4096
# The select loop can be used in two modes:
# - setting ""infinite"" will select without a timeout, hogging a thread
# - setting a positive timeout will do a bounded select call,
# enabling sharing of a single thread between multiple selectors
# (in this case you will have to use a different configuration for the
# selector-dispatcher, e.g. using ""type=Dispatcher"" with size 1)
# - setting it to zero means polling, i.e. calling selectNow()
select-timeout = infinite
# When trying to assign a new connection to a selector and the chosen
# selector is at full capacity, retry selector choosing and assignment
# this many times before giving up
selector-association-retries = 10
# The maximum number of datagrams that are read in one go,
# higher numbers decrease latency, lower numbers increase fairness on
# the worker-dispatcher
receive-throughput = 3
# The number of bytes per direct buffer in the pool used to read or write
# network data from the kernel.
direct-buffer-size = 128 KiB
# The maximal number of direct buffers kept in the direct buffer pool for
# reuse.
direct-buffer-pool-limit = 1000
# The maximum number of bytes delivered by a `Received` message. Before
# more data is read from the network the connection actor will try to
# do other work.
received-message-size-limit = unlimited
# Enable fine grained logging of what goes on inside the implementation.
# Be aware that this may log more than once per message sent to the actors
# of the tcp implementation.
trace-logging = off
# Fully qualified config path which holds the dispatcher configuration
# to be used for running the select() calls in the selectors
selector-dispatcher = ""akka.io.pinned-dispatcher""
# Fully qualified config path which holds the dispatcher configuration
# for the read/write worker actors
worker-dispatcher = ""akka.actor.default-dispatcher""
# Fully qualified config path which holds the dispatcher configuration
# for the selector management actors
management-dispatcher = ""akka.actor.default-dispatcher""
}
}
}
";
Node res = Parser.Parse(data);
Console.WriteLine(res);
Console.ReadLine();
}
}
public static class Parser
{
public static Node Parse(string data)
{
var nodes = new Stack<Node>();
var roots = new Stack<Node>();
var node = new Node();
Node root = node;
int i = 0;
nodes.Push(node);
roots.Push(node);
while (i < data.Length)
{
Token t = ConsumeNext(data, ref i);
if (t.Type == TokenType.EoF)
break;
if (t.Type == TokenType.Identifier)
{
Node newNode = node.CreateChild(t.Value);
node = newNode;
nodes.Push(node);
}
else if (t.Type == TokenType.Dot)
{
Token id = ConsumeNext(data, ref i);
Node newNode = node.CreateChild(id.Value);
node = newNode;
nodes.Push(node);
}
else if (t.Type == TokenType.Eq)
{
string v = ConsumeLine(data, ref i);
node.Value = v;
while (nodes.Peek() != roots.Peek())
{
nodes.Pop();
}
node = nodes.Peek();
}
else if (t.Type == TokenType.LBrace)
{
roots.Push(node);
}
else if (t.Type == TokenType.RBrace)
{
node = roots.Pop();
}
}
return root;
}
private static string ConsumeLine(string data, ref int i)
{
ConsumeWhitespace(data, ref i);
var sb = new StringBuilder();
while (i < data.Length)
{
char c = data[i];
if (c == '\n')
break;
if (c != '\r')
sb.Append(c);
i++;
}
return sb.ToString().Trim();
}
private static Token ConsumeNext(string data, ref int i)
{
ConsumeWhitespace(data, ref i);
switch (data[i])
{
case '.':
case '{':
case '}':
case '=':
return ConsumeSingleChar(data, ref i);
case '#':
return ConsumeComment(data, ref i);
case '"':
return ConsumeString(data, ref i);
default:
if (IsIdentifierChar(data[i]))
return ConsumeIdentifier(data, ref i);
if (i == data.Length - 1)
return new Token(TokenType.EoF);
throw new Exception("unknown token");
}
}
private static Token ConsumeSingleChar(string data, ref int i)
{
return new Token((TokenType) data[i++]);
}
private static Token ConsumeComment(string data, ref int i)
{
ConsumeLine(data, ref i);
return new Token(TokenType.Comment);
}
private static Token ConsumeIdentifier(string data, ref int i)
{
int start = i;
while (IsIdentifierChar(data[i]) && i < data.Length)
{
i++;
}
return new Token(data.Substring(start,i - start));
}
private static bool IsIdentifierChar(char c)
{
return (char.IsLetterOrDigit(c) || c == '-' || c == '/');
}
private static Token ConsumeString(string data, ref int i)
{
int start = i++;
while ((data[i] != '"') && i < data.Length)
{
i++;
}
i++;
return new Token(data.Substring(start,i-start));
}
private static void ConsumeWhitespace(string data, ref int i)
{
while (char.IsWhiteSpace(data[i]) && i < data.Length - 1)
{
i++;
}
}
}
public enum TokenType
{
Comment=1,
Identifier=2,
Eq=61,
LBrace=123,
RBrace=125,
Dot = 46,
EoF,
}
public class Token
{
public Token(TokenType type)
{
Type = type;
}
public Token(string value)
{
Type = TokenType.Identifier;
Value = value;
}
public string Value { get; set; }
public TokenType Type { get; set; }
}
public class Node
{
private readonly Dictionary<string, Node> _children = new Dictionary<string, Node>();
public Node()
{
Id = "";
}
public string Id { get; set; }
public string Value { get; set; }
public IEnumerable<Node> Children
{
get { return _children.Values; }
}
public Node CreateChild(string id)
{
if (_children.ContainsKey(id))
{
return _children[id];
}
var child = new Node
{
Id = id,
};
_children.Add(id, child);
return child;
}
public override string ToString()
{
return ToString(0);
}
public string ToString(int indent)
{
var t = new string(' ', indent*2);
var sb = new StringBuilder();
if (Value != null)
{
sb.AppendFormat("{0}{1} = {2}", t, Id, Value);
return sb.ToString();
}
sb.AppendLine(t + Id + " {");
foreach (Node child in Children)
{
sb.AppendLine(child.ToString(indent + 1));
}
sb.Append(t + "}");
return sb.ToString();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment