private IEnumerable <Parselet> LeftSquare(Token <R> token) { var tagToken = reader.ReadToken(); switch (tagToken.ID) { default: compiler.SyntaxError(tagToken, "Expected function name, subroutine, regex or a script."); break; case R.Text: yield return(Parselet.GetParselet("FunctionText", tagToken, AddToOutput)); break; case R.Regex: yield return(Parselet.GetParselet("FunctionRegex", tagToken, AddToOutput)); break; case R.At: AddToOutput(compiler.ReadScript()); break; case R.Dollar: yield return(Parselet.GetParselet("FunctionSubroutine", tagToken, AddToOutput)); break; } }
public static void Load(bool forceNewLoad = false) { if (loaded && !forceNewLoad) { return; } // scan the Compiler.Parselets namespace for all parselets, create instances of them store them in a dictionary // it's clean, it's super easy to extend. a single statement loads them all from the namespace and sets them up right // it's slow and may be a memory hog with many parselets // maybe create the instances of the parselets as needed? #if DEBUG System.Diagnostics.Stopwatch timer = System.Diagnostics.Stopwatch.StartNew(); #endif tokenTypeParseletNameDict = new Dictionary <R, string>(); parseletNameDict = new Dictionary <string, Parselet>(); var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && !t.IsNested && // for some reason makes sure the internal enumerable type isn't included !t.IsAbstract && // makes sure we don't load this base class t.Namespace == "Rant.Engine.Compiler.Parselets"); foreach (var type in types) { #if UNITY if (type.GetCustomAttributes(typeof(DefaultParseletAttribute), true).Cast <DefaultParseletAttribute>().FirstOrDefault() != null) #else if (type.GetCustomAttribute <DefaultParseletAttribute>() != null) #endif { if (defaultParselet != null) { throw new RantInternalException($"Cannot define {type.Name} as default parselet: {defaultParselet.GetType().Name} is already defined as default parselet."); } defaultParselet = (Parselet)Activator.CreateInstance(type); continue; } var instance = (Parselet)Activator.CreateInstance(type); // here we just hope that the parselet handles itself properly } if (defaultParselet == null) { throw new RantInternalException("Default parselet missing/not loaded."); } loaded = true; #if DEBUG timer.Stop(); Console.WriteLine($"{parseletNameDict.Count} parselets from {types.Count()} classes and {tokenTypeParseletNameDict.Count} token name associations loaded in {timer.ElapsedMilliseconds}ms"); #endif }
private IEnumerable <Parselet> BlockWeight(Token <R> token) { Token <R> funcToken = null; var actions = new List <RantAction>(); while (!reader.End) { funcToken = reader.ReadToken(); if (funcToken.ID == R.RightParen) { reader.SkipSpace(); if (!actions.Any()) { compiler.SyntaxError(funcToken, "Expected weight value"); } RantAction weightAction; // probably a number if (actions[0] is RAText) { if (actions.Count > 1) { if (!(actions[1] is RAText) || ((RAText)actions[1]).Text != "." || actions.Count != 3) { compiler.SyntaxError(actions[1].Range, "Invalid block weight value."); } weightAction = new RAText(actions[0].Range, (actions[0] as RAText).Text + (actions[1] as RAText).Text + (actions[2] as RAText).Text); } else { weightAction = actions[0]; } } else { weightAction = new RASequence(actions, funcToken); } AddToOutput(weightAction); yield break; } yield return(Parselet.GetParselet(funcToken, actions.Add)); } // TODO: this token is "our" (the one that result is us being yielded) token. maybe have the 'fromToken' passed? compiler.SyntaxError(token, "Unterminated function: unexpected end of file"); }
public static void Load(bool forceNewLoad = false) { if (loaded && !forceNewLoad) return; // scan the Compiler.Parselets namespace for all parselets, create instances of them store them in a dictionary // it's clean, it's super easy to extend. a single statement loads them all from the namespace and sets them up right // it's slow and may be a memory hog with many parselets // maybe create the instances of the parselets as needed? #if DEBUG System.Diagnostics.Stopwatch timer = System.Diagnostics.Stopwatch.StartNew(); #endif tokenTypeParseletNameDict = new Dictionary<R, string>(); parseletNameDict = new Dictionary<string, Parselet>(); var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && !t.IsNested && // for some reason makes sure the internal enumerable type isn't included !t.IsAbstract && // makes sure we don't load this base class t.Namespace == "Rant.Engine.Compiler.Parselets"); foreach (var type in types) { #if UNITY if (type.GetCustomAttributes(typeof(DefaultParseletAttribute), true).Cast<DefaultParseletAttribute>().FirstOrDefault() != null) #else if (type.GetCustomAttribute<DefaultParseletAttribute>() != null) #endif { if (defaultParselet != null) throw new RantInternalException($"Cannot define {type.Name} as default parselet: {defaultParselet.GetType().Name} is already defined as default parselet."); defaultParselet = (Parselet)Activator.CreateInstance(type); continue; } var instance = (Parselet)Activator.CreateInstance(type); // here we just hope that the parselet handles itself properly } if (defaultParselet == null) throw new RantInternalException("Default parselet missing/not loaded."); loaded = true; #if DEBUG timer.Stop(); Console.WriteLine($"{parseletNameDict.Count} parselets from {types.Count()} classes and {tokenTypeParseletNameDict.Count} token name associations loaded in {timer.ElapsedMilliseconds}ms"); #endif }
private IEnumerable <Parselet> LeftCurly(Token <R> token) { reader.SkipSpace(); // LOOK AT ME. I'M THE COMPILER NOW Token <R> readToken = null; var actions = new List <RantAction>(); var sequences = new List <RantAction>(); List <_ <int, double> > constantWeights = null; List <_ <int, RantAction> > dynamicWeights = null; while (!reader.End) { readToken = reader.ReadToken(); // TODO: kinda stupid having this handle it's own whitespace when we have a parselet for whitespace if (readToken.ID == R.Whitespace) { switch (reader.PeekType()) { case R.RightCurly: case R.Pipe: continue; } } else if (readToken.ID == R.LeftParen) // weight { RantAction weightAction = null; // i like this AddToOutput thing because it's just a delegate that takes in a RantAction. // it can do anything with the RantAction, in this case it sets it to our weightAction // :> yield return(Parselet.GetParselet("BlockWeight", readToken, a => weightAction = a)); constantWeights = constantWeights ?? new List <_ <int, double> >(); dynamicWeights = dynamicWeights ?? new List <_ <int, RantAction> >(); if (weightAction is RAText) // constant { var strWeight = (weightAction as RAText).Text; double d; if (!Util.ParseDouble(strWeight, out d)) { compiler.SyntaxError(weightAction.Range, $"Invalid weight value '{strWeight}'."); } constantWeights.Add(_.Create(sequences.Count, d)); } else // dynamic { // TODO: there's some weird issue going on with doubles being seen as dynamic weights dynamicWeights.Add(_.Create(sequences.Count, weightAction)); } continue; } else if (readToken.ID == R.Pipe) { // add action to block and continue sequences.Add(actions.Count == 1 ? actions[0] : new RASequence(actions, readToken)); reader.SkipSpace(); actions.Clear(); continue; } else if (readToken.ID == R.RightCurly) { // add action to block and return sequences.Add(actions.Count == 1 ? actions[0] : new RASequence(actions, readToken)); AddToOutput(new RABlock(Stringe.Range(token, readToken), sequences, dynamicWeights, constantWeights)); yield break; } yield return(Parselet.GetParselet(readToken, actions.Add)); } compiler.SyntaxError(token, "Unterminated block: unexpected end of file."); }