private IEnumerator <Parser> ReadArguments(RantCompiler compiler, TokenReader reader, List <RST> arguments) { var actions = new List <RST>(); Action <RST> argActionCallback = action => actions.Add(action); compiler.SetNextActionCallback(argActionCallback); compiler.AddContext(CompileContext.FunctionEndContext); compiler.AddContext(CompileContext.ArgumentSequence); while (compiler.NextContext == CompileContext.ArgumentSequence) { reader.SkipSpace(); var startToken = reader.PeekToken(); yield return(Get <SequenceParser>()); // Don't wrap single nodes in a sequence, it's unnecessary if (actions.Count == 1) { arguments.Add(actions[0]); } else { arguments.Add(new RstSequence(actions, startToken.ToLocation())); } actions.Clear(); } compiler.LeaveContext(); }
internal RantProgram(string name, RantProgramOrigin type, string code) { Name = name; Type = type; Code = code; var compiler = new RantCompiler(name, code); SyntaxTree = compiler.Compile(); }
internal RantPattern(string name, RantPatternOrigin type, string code) { Name = name; Type = type; Code = code; var compiler = new RantCompiler(name, code); Action = compiler.Compile(); Module = compiler.HasModule ? compiler.Module : null; }
private void InternalSetCompilerAndReader(RantCompiler compiler, TokenReader reader) { if (compiler == null) { throw new RantInternalException($"{GetType().Name}.InternalSetCompilerAndReader(): Given compiler is null."); } if (reader == null) { throw new RantInternalException($"{GetType().Name}.InternalSetCompilerAndReader(): Given token reader is null."); } this.compiler = compiler; this.reader = reader; }
private IEnumerator <Parser> ParseFunction(RantCompiler compiler, CompileContext context, TokenReader reader, Action <RST> actionCallback) { var functionName = reader.Read(R.Text, "acc-function-name"); var arguments = new List <RST>(); if (reader.PeekType() == R.Colon) { reader.ReadToken(); var iterator = ReadArguments(compiler, reader, arguments); while (iterator.MoveNext()) { yield return(iterator.Current); } compiler.SetNextActionCallback(actionCallback); } else { reader.Read(R.RightSquare); } RantFunctionSignature sig = null; if (functionName.Value != null) { if (!RantFunctionRegistry.FunctionExists(functionName.Value)) { compiler.SyntaxError(functionName, false, "err-compiler-nonexistent-function", functionName.Value); yield break; } if ((sig = RantFunctionRegistry.GetFunction(functionName.Value, arguments.Count)) == null) { compiler.SyntaxError(functionName, false, "err-compiler-nonexistent-overload", functionName.Value, arguments.Count); yield break; } actionCallback(new RstFunction(functionName.ToLocation(), sig, arguments)); } }
// NOTE: one thing i don't really like is having to create a completely new compiler for each pattern you run. // figure out a way to create one compiler at the start, and use that for all patterns? private bool MatchingCompilerAndReader(RantCompiler compiler, TokenReader reader) => this.compiler == compiler && this.reader == reader;
public static void SetCompilerAndReader(RantCompiler c, TokenReader r) { sCompiler = c; sReader = r; }
public abstract IEnumerator <Parser> Parse(RantCompiler compiler, CompileContext context, TokenReader reader, Action <RST> actionCallback);
public override IEnumerator <Parser> Parse(RantCompiler compiler, CompileContext context, TokenReader reader, Action <RST> actionCallback) { var nextType = reader.PeekType(); var tagStart = reader.PrevToken; // replacer switch (nextType) { case R.Regex: { var regex = reader.Read(R.Regex, "acc-replacer-regex"); var options = RegexOptions.Compiled | RegexOptions.ExplicitCapture; if (reader.IsNext(R.RegexFlags)) { var flagsToken = reader.ReadToken(); foreach (char flag in flagsToken.Value) { switch (flag) { case 'i': options |= RegexOptions.IgnoreCase; break; case 'm': options |= RegexOptions.Multiline; break; } } } reader.Read(R.Colon); var arguments = new List <RST>(); var iterator = ReadArguments(compiler, reader, arguments); while (iterator.MoveNext()) { yield return(iterator.Current); } compiler.SetNextActionCallback(actionCallback); if (arguments.Count != 2) { compiler.SyntaxError(tagStart, reader.PrevToken, false, "err-compiler-replacer-argcount"); yield break; } actionCallback(new RstReplacer(regex.ToLocation(), new Regex(regex.Value, options), arguments[0], arguments[1])); } break; case R.Dollar: { reader.ReadToken(); var e = ParseSubroutine(compiler, context, reader, actionCallback); while (e.MoveNext()) { yield return(e.Current); } } break; default: { var e = ParseFunction(compiler, context, reader, actionCallback); while (e.MoveNext()) { yield return(e.Current); } } break; } }
public override IEnumerator <Parser> Parse(RantCompiler compiler, CompileContext context, TokenReader reader, Action <RantAction> actionCallback) { throw new NotImplementedException(); }
private void ReadCarriers(TokenReader reader, Carrier carrier, RantCompiler compiler) { while (!reader.End) { var token = reader.ReadLooseToken(); switch (token.Type) { // match carrier case R.Equal: { var name = reader.Read(R.Text, "acc-carrier-name"); if (name.Value != null) { carrier.AddComponent(CarrierComponentType.Match, name.Value); } } break; // associative and match associative, // disassociative and match disassociative // divergent and match-divergent // relational and match-relational case R.At: { var carrierType = CarrierComponentType.Associative; // disassociative if (reader.PeekToken().Type == R.Exclamation) { carrierType = CarrierComponentType.Dissociative; reader.ReadToken(); } // divergent else if (reader.PeekToken().Type == R.Plus) { carrierType = CarrierComponentType.Divergent; reader.ReadToken(); } else if (reader.PeekToken().Type == R.Question) { carrierType = CarrierComponentType.Relational; reader.ReadToken(); } // match if (reader.PeekToken().Type == R.Equal) { switch (carrierType) { case CarrierComponentType.Associative: carrierType = CarrierComponentType.MatchAssociative; break; case CarrierComponentType.Dissociative: carrierType = CarrierComponentType.MatchDissociative; break; case CarrierComponentType.Divergent: carrierType = CarrierComponentType.MatchDivergent; break; case CarrierComponentType.Relational: carrierType = CarrierComponentType.MatchRelational; break; } reader.ReadToken(); } var name = reader.Read(R.Text, "acc-carrier-name"); if (name.Value != null) { carrier.AddComponent(carrierType, name.Value); } } break; // unique and match unique case R.Exclamation: { var carrierType = CarrierComponentType.Unique; // match unique if (reader.PeekToken().Type == R.Equal) { carrierType = CarrierComponentType.MatchUnique; reader.ReadToken(); } var name = reader.Read(R.Text, "acc-carrier-name"); if (name.Value != null) { carrier.AddComponent(carrierType, name.Value); } } break; // rhyming case R.Ampersand: { var name = reader.Read(R.Text, "acc-carrier-name"); if (name.Value != null) { carrier.AddComponent(CarrierComponentType.Rhyme, name.Value); } } break; // we're done, go away case R.RightAngle: return; default: compiler.SyntaxError(token, false, "err-compiler-unexpected-token"); break; } } }
private void InternalSetCompilerAndReader(RantCompiler compiler, TokenReader reader) { if (compiler == null) throw new RantInternalException($"{GetType().Name}.InternalSetCompilerAndReader(): Given compiler is null."); if (reader == null) throw new RantInternalException($"{GetType().Name}.InternalSetCompilerAndReader(): Given token reader is null."); this.compiler = compiler; this.reader = reader; }
public override IEnumerator <Parser> Parse(RantCompiler compiler, CompileContext context, TokenReader reader, Action <RST> actionCallback) { Token token; while (!reader.End) { token = reader.ReadToken(); switch (token.Type) { case R.LeftAngle: yield return(Get <QueryParser>()); break; case R.LeftSquare: yield return(Get <TagParser>()); break; case R.LeftCurly: reader.SkipSpace(); yield return(Get <BlockParser>()); break; case R.Pipe: if (context == CompileContext.BlockSequence) { yield break; } goto default; // Print it if we're not in a block case R.RightCurly: if (context == CompileContext.BlockSequence) { compiler.LeaveContext(); yield break; } compiler.SyntaxError(token, false, "err-compiler-unexpected-token", token.Value); break; // end of argument case R.Semicolon: if (context == CompileContext.ArgumentSequence) { yield break; } // this is probably just a semicolon in text actionCallback(new RstText(token.Value, token.ToLocation())); break; case R.RightSquare: // end of arguments / direct object in query switch (context) { case CompileContext.ArgumentSequence: case CompileContext.SubroutineBody: case CompileContext.QueryComplement: compiler.LeaveContext(); yield break; } compiler.SyntaxError(token, false, "err-compiler-unexpected-token", token.Value); break; case R.RightAngle: compiler.SyntaxError(token, false, "err-compiler-unexpected-token", token.Value); break; // the end of a block weight, maybe case R.RightParen: if (context == CompileContext.BlockWeight) { reader.SkipSpace(); compiler.LeaveContext(); yield break; } actionCallback(new RstText(token.Value, token.ToLocation())); break; case R.Whitespace: switch (context) { case CompileContext.BlockSequence: switch (reader.PeekType()) { case R.Pipe: case R.RightCurly: continue; // Ignore whitespace at the end of block elements } goto default; default: actionCallback(new RstText(token.Value, token.ToLocation())); break; } break; case R.EscapeSequenceChar: // Handle escape sequences actionCallback(new RstEscape(token.ToLocation(), 1, false, token.Value[0])); break; case R.EscapeSequenceUnicode: { short codePoint; if (!short.TryParse(token.Value, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out codePoint)) { compiler.SyntaxError(reader.PrevToken, false, "err-compiler-invalid-escape-unicode", reader.PrevToken.Value); break; } actionCallback(new RstEscape(token.ToLocation(), 1, true, Convert.ToChar(codePoint))); break; } case R.EscapeSequenceSurrogatePair: { uint surrogatePairCodePoint; if (!uint.TryParse(token.Value, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out surrogatePairCodePoint) || surrogatePairCodePoint < 0x10000) { compiler.SyntaxError(reader.PrevToken, false, "err-compiler-invalid-escape-surrogate", token.Value); break; } surrogatePairCodePoint -= 0x10000; ushort highCodePoint = (ushort)(0xD800 + ((surrogatePairCodePoint & 0xFFC00) >> 10)); ushort lowCodePoint = (ushort)(0xDC00 + (surrogatePairCodePoint & 0x003FF)); char low, high; if (!char.IsSurrogatePair(high = Convert.ToChar(highCodePoint), low = Convert.ToChar(lowCodePoint))) { compiler.SyntaxError(reader.PrevToken, false, "err-compiler-invalid-escape-surrogate", token.Value); break; } actionCallback(new RstEscape(token.ToLocation(), 1, true, high, low)); break; } case R.EscapeSequenceQuantifier: { if (!Util.ParseInt(token.Value, out int quantity) || quantity <= 0) { compiler.SyntaxError(token, false, "err-compiler-escape-bad-quantity"); break; } switch (reader.PeekType()) { case R.EscapeSequenceChar: actionCallback(new RstEscape(token.ToLocation(), quantity, false, reader.ReadToken().Value[0])); break; case R.EscapeSequenceUnicode: { short codePoint; if (!short.TryParse(reader.ReadToken().Value, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out codePoint)) { compiler.SyntaxError(reader.PrevToken, false, "err-compiler-invalid-escape-unicode", reader.PrevToken.Value); break; } actionCallback(new RstEscape(token.ToLocation(), quantity, true, Convert.ToChar(codePoint))); break; } case R.EscapeSequenceSurrogatePair: { var pairToken = reader.ReadToken(); uint surrogatePairCodePoint; if (!uint.TryParse(pairToken.Value, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out surrogatePairCodePoint) || surrogatePairCodePoint < 0x10000) { compiler.SyntaxError(reader.PrevToken, false, "err-compiler-invalid-escape-surrogate", pairToken.Value); break; } surrogatePairCodePoint -= 0x10000; ushort highCodePoint = (ushort)(0xD800 + ((surrogatePairCodePoint & 0xFFC00) >> 10)); ushort lowCodePoint = (ushort)(0xDC00 + (surrogatePairCodePoint & 0x3FF)); char low, high; if (!char.IsSurrogatePair(high = Convert.ToChar(highCodePoint), low = Convert.ToChar(lowCodePoint))) { compiler.SyntaxError(reader.PrevToken, false, "err-compiler-invalid-escape-surrogate", pairToken.Value); break; } actionCallback(new RstEscape(token.ToLocation(), quantity, true, high, low)); break; } } break; } case R.EOF: if (context != CompileContext.DefaultSequence) { compiler.SyntaxError(token, true, "err-compiler-eof"); } yield break; default: // Handle text actionCallback(new RstText(token.Value, token.ToLocation())); break; } } if (reader.End && context != CompileContext.DefaultSequence) { compiler.SyntaxError(reader.PrevToken, true, "err-compiler-eof"); } }
public override IEnumerator <Parser> Parse(RantCompiler compiler, CompileContext context, TokenReader reader, Action <RantAction> actionCallback) { Token <R> token; while (!reader.End) { token = reader.ReadToken(); switch (token.ID) { case R.LeftAngle: yield return(Get <QueryParser>()); break; case R.LeftSquare: // TODO: Tags break; case R.LeftCurly: reader.SkipSpace(); // Tell the compiler that we're about to read a block compiler.SetNextContext(CompileContext.BlockSequence); yield return(Get <SequenceParser>()); break; case R.Pipe: if (context == CompileContext.BlockSequence) { // TODO: Complete the element and start reading the next one } else { goto default; // Print it if we're not in a block } break; case R.RightCurly: if (context == CompileContext.BlockSequence) { // TODO: Complete the element and terminate the block } else { compiler.SyntaxError(token, "Unexpected block terminator"); } break; case R.Whitespace: switch (context) { case CompileContext.DefaultSequence: actionCallback(new RAText(token)); break; case CompileContext.BlockSequence: switch (reader.PeekType()) { case R.Pipe: case R.RightCurly: continue; // Ignore whitespace at the end of block elements } break; } break; default: // Handle text actionCallback(new RAText(token)); break; } } }
private IEnumerator <Parser> ParseSubroutine(RantCompiler compiler, CompileContext context, TokenReader reader, Action <RST> actionCallback) { // subroutine definition if (reader.TakeLoose(R.LeftSquare, false)) { if (reader.TakeLoose(R.Period)) { compiler.HasModule = true; } var subroutineName = reader.ReadLoose(R.Text, "acc-subroutine-name"); var subroutine = new RstDefineSubroutine(subroutineName.ToLocation()) { Parameters = new Dictionary <string, SubroutineParameterType>(), Name = subroutineName.Value }; if (reader.PeekLooseToken().Type == R.Colon) { reader.ReadLooseToken(); do { var type = SubroutineParameterType.Greedy; if (reader.TakeLoose(R.At)) { type = SubroutineParameterType.Loose; } subroutine.Parameters[reader.ReadLoose(R.Text, "acc-arg-name").Value] = type; } while (reader.TakeLoose(R.Semicolon, false)); } reader.ReadLoose(R.RightSquare); var bodyStart = reader.ReadLoose(R.Colon); var actions = new List <RST>(); Action <RST> bodyActionCallback = action => actions.Add(action); compiler.AddContext(CompileContext.SubroutineBody); compiler.SetNextActionCallback(bodyActionCallback); yield return(Get <SequenceParser>()); compiler.SetNextActionCallback(actionCallback); if (actions.Count == 1) { subroutine.Body = actions[0]; } else { subroutine.Body = new RstSequence(actions, bodyStart.ToLocation()); } actionCallback(subroutine); } else { // subroutine call var subroutineName = reader.Read(R.Text, "acc-subroutine-name"); string moduleFunctionName = null; if (reader.TakeLoose(R.Period, false)) { moduleFunctionName = reader.Read(R.Text, "module function name").Value; } var arguments = new List <RST>(); if (reader.PeekType() == R.Colon) { reader.ReadToken(); var iterator = ReadArguments(compiler, reader, arguments); while (iterator.MoveNext()) { yield return(iterator.Current); } compiler.SetNextActionCallback(actionCallback); } else { reader.Read(R.RightSquare); } var subroutine = new RstCallSubroutine(subroutineName.Value, subroutineName.ToLocation(), moduleFunctionName) { Arguments = arguments }; actionCallback(subroutine); } }
public override IEnumerator <Parser> Parse(RantCompiler compiler, CompileContext context, TokenReader reader, Action <RST> actionCallback) { var tableName = reader.ReadLoose(R.Text, "acc-table-name"); var query = new Query(); query.Name = tableName.Value; query.Carrier = new Carrier(); query.Exclusive = reader.TakeLoose(R.Dollar); bool subtypeRead = false; bool pluralSubtypeRead = false; bool complementRead = false; bool endOfQueryReached = false; while (!reader.End && !endOfQueryReached) { var token = reader.ReadLooseToken(); switch (token.Type) { // read subtype case R.Period: if (reader.Take(R.Period)) // Plural subtype { if (pluralSubtypeRead) { compiler.SyntaxError(token, false, "err-compiler-multiple-pl-subtypes"); reader.Read(R.Text, "acc-pl-subtype-name"); break; } query.PluralSubtype = reader.Read(R.Text, "acc-pl-subtype-name").Value; pluralSubtypeRead = true; break; } // if there's already a subtype, throw an error and ignore it if (subtypeRead) { compiler.SyntaxError(token, false, "err-compiler-multiple-subtypes"); reader.Read(R.Text, "acc-subtype-name"); break; } query.Subtype = reader.Read(R.Text, "acc-subtype-name").Value; subtypeRead = true; break; // complement case R.LeftSquare: { if (complementRead) { compiler.SyntaxError(token, false, "err-compiler-multiple-complements"); } var seq = new List <RST>(); compiler.AddContext(CompileContext.QueryComplement); compiler.SetNextActionCallback(seq.Add); yield return(Get <SequenceParser>()); compiler.SetNextActionCallback(actionCallback); query.Complement = new RstSequence(seq, token.ToLocation()); complementRead = true; } break; // read class filter case R.Hyphen: { ClassFilter classFilter; if ((classFilter = query.GetClassFilters().FirstOrDefault()) == null) { classFilter = new ClassFilter(); query.AddFilter(classFilter); } var filterSwitches = new List <ClassFilterRule>(); do { reader.SkipSpace(); bool blacklist = false; // check if it's a blacklist filter if (reader.PeekType() == R.Exclamation) { blacklist = true; reader.ReadToken(); } var classFilterName = reader.Read(R.Text, "acc-class-filter-rule"); if (classFilterName.Value == null) { continue; } var rule = new ClassFilterRule(classFilterName.Value, !blacklist); filterSwitches.Add(rule); } while (reader.TakeLoose(R.Pipe)); //fyi: this feature is undocumented classFilter.AddRuleSwitch(filterSwitches.ToArray()); break; } // read regex filter case R.Without: case R.Question: { reader.SkipSpace(); bool blacklist = token.Type == R.Without; var regexFilter = reader.Read(R.Regex, "acc-regex-filter-rule"); var options = RegexOptions.Compiled | RegexOptions.ExplicitCapture; if (reader.IsNext(R.RegexFlags)) { var flagsToken = reader.ReadToken(); foreach (char flag in flagsToken.Value) { switch (flag) { case 'i': options |= RegexOptions.IgnoreCase; break; case 'm': options |= RegexOptions.Multiline; break; } } } if (regexFilter.Value == null) { break; } query.AddFilter(new RegexFilter(new Regex(regexFilter.Value, options), !blacklist)); } break; // read syllable range case R.LeftParen: // There are four possible types of values in a syllable range: // (a), (a-), (-b), (a-b) // either (a), (a-), or (a-b) if (reader.PeekLooseToken().Type == R.Text) { var firstNumberToken = reader.ReadLooseToken(); int firstNumber; if (!Util.ParseInt(firstNumberToken.Value, out firstNumber)) { compiler.SyntaxError(firstNumberToken, false, "err-compiler-bad-sylrange-value"); } // (a-) or (a-b) if (reader.PeekLooseToken().Type == R.Hyphen) { reader.ReadLooseToken(); // (a-b) if (reader.PeekLooseToken().Type == R.Text) { var secondNumberToken = reader.ReadLooseToken(); int secondNumber; if (!Util.ParseInt(secondNumberToken.Value, out secondNumber)) { compiler.SyntaxError(secondNumberToken, false, "err-compiler-bad-sylrange-value"); } query.AddFilter(new RangeFilter(firstNumber, secondNumber)); } // (a-) else { query.AddFilter(new RangeFilter(firstNumber, null)); } } // (a) else { query.AddFilter(new RangeFilter(firstNumber, firstNumber)); } } // (-b) else if (reader.PeekLooseToken().Type == R.Hyphen) { reader.ReadLooseToken(); var secondNumberToken = reader.ReadLoose(R.Text, "acc-syllable-range-value"); int secondNumber; if (!Util.ParseInt(secondNumberToken.Value, out secondNumber)) { compiler.SyntaxError(secondNumberToken, false, "err-compiler-bad-sylrange-value"); } query.AddFilter(new RangeFilter(null, secondNumber)); } // () else if (reader.PeekLooseToken().Type == R.RightParen) { compiler.SyntaxError(token, false, "err-compiler-empty-sylrange"); } // (something else) else { var errorToken = reader.ReadLooseToken(); compiler.SyntaxError(errorToken, false, "err-compiler-unknown-sylrange-token", errorToken.Value); reader.TakeAllWhile(t => !reader.IsNext(R.RightParen)); } reader.ReadLoose(R.RightParen); break; // read carriers case R.DoubleColon: ReadCarriers(reader, query.Carrier, compiler); // this should be the last part of the query, so go to the end endOfQueryReached = true; break; // end of query case R.RightAngle: endOfQueryReached = true; break; case R.Whitespace: break; default: compiler.SyntaxError(token, false, "err-compiler-unexpected-token"); break; } } if (!endOfQueryReached) { compiler.SyntaxError(reader.PrevToken, true, "err-compiler-eof"); } if (tableName.Value != null) { actionCallback(new RstQuery(query, tableName.ToLocation())); } }
public override IEnumerator <Parser> Parse(RantCompiler compiler, CompileContext context, TokenReader reader, Action <RST> actionCallback) { var blockStartToken = reader.PrevLooseToken; var items = new List <RST>(); var actions = new List <RST>(); // "why are these not lists or arrays" i yell into the void, too lazy to find out why List <_ <int, double> > constantWeights = null; List <_ <int, RST> > dynamicWeights = null; int blockNumber = 0; void itemCallback(RST action) => actions.Add(action); compiler.AddContext(CompileContext.BlockEndSequence); compiler.AddContext(CompileContext.BlockSequence); while (compiler.NextContext == CompileContext.BlockSequence) { // block weight if (reader.PeekLooseToken().Type == R.LeftParen) { constantWeights = constantWeights ?? (constantWeights = new List <_ <int, double> >()); dynamicWeights = dynamicWeights ?? (dynamicWeights = new List <_ <int, RST> >()); var firstToken = reader.ReadLooseToken(); var sequence = new List <RST>(); void weightCallback(RST rst) => sequence.Add(rst); compiler.SetNextActionCallback(weightCallback); compiler.AddContext(CompileContext.BlockWeight); yield return(Get <SequenceParser>()); // Constant if (sequence.TrueForAll(rst => rst is RstText)) { var sb = new StringBuilder(); foreach (var rst in sequence) { sb.Append((rst as RstText).Text); } string txt = sb.ToString(); if (!Util.ParseDouble(txt, out double doubleValue)) { compiler.SyntaxError(reader.PrevLooseToken, false, "err-compiler-invalid-constweight"); } else { constantWeights.Add(new _ <int, double>(blockNumber, doubleValue)); } } // Dynamic else { if (sequence.Count == 0) { compiler.SyntaxError(firstToken, false, "err-compiler-empty-weight"); } else { dynamicWeights.Add(new _ <int, RST>(blockNumber, new RstSequence(sequence, sequence[0].Location))); } } } reader.SkipSpace(); compiler.SetNextActionCallback(itemCallback); var startToken = reader.PeekToken(); yield return(Get <SequenceParser>()); // Don't wrap single nodes in a sequence, it's unnecessary items.Add(actions.Count == 1 ? actions[0] : new RstSequence(actions, startToken.ToLocation())); actions.Clear(); blockNumber++; } compiler.LeaveContext(); compiler.SetNextActionCallback(actionCallback); actionCallback(new RstBlock(blockStartToken.ToLocation(), items, dynamicWeights, constantWeights)); }