예제 #1
0
        public static Vars Parse(string[] lines, bool ignoreWhiteSpace = false)
        {
            Vars vars = new Vars();

            for (int i = 0; i < lines.Length; i++)
            {
                string line = lines[i];

                // skip comments
                if (IsComment(line))
                    continue;

                line = RemoveInlineComment(line);
                line = RemoveExportKeyword(line);

                string[] keyValuePair = line.Split('=');

                // skip malformed lines
                if (keyValuePair.Length != 2)
                    continue;

                if (!ignoreWhiteSpace)
                {
                    keyValuePair[0] = keyValuePair[0].Trim();
                    keyValuePair[1] = keyValuePair[1].Trim();
                }

                vars.Add(
                    keyValuePair[0],
                    keyValuePair[1]
                );
            }

            return vars;
        }
예제 #2
0
        public GCallStmt(string text)
        {
            this.Text = text;
            StmtType  = GimpleStmtType.GCALL;
            Pattern   = myPattern;
            var match = Regex.Match(text, myPattern);

            FuncName = match.Groups["funcname"].Value;
            string strArgs = match.Groups["args"].Value;
            var    matches = Regex.Matches(strArgs, @"^(\&\"".*\""\[\d*\])|((\"""".*\"""")|([\w\.]*)(,\s*(\"""".*\"""")|([\w\.]))*)$");

            foreach (Match m in matches)
            {
                if (string.IsNullOrWhiteSpace(m.Value))
                {
                    continue;
                }
                Args.Add(m.Value);
                if (IsValidIdentifier(m.Value))
                {
                    Vars.Add(m.Value);
                }
            }
        }
예제 #3
0
 //Семантический разбор формулы
 private void ParseFormula()//расчетное или управляющее выражение
 {
     Vars.Add("расчет", Vars.Add("calc", new TablikVar("расчет")));
     Expr1 = (TablikListNode) new ExprParsing(Keeper, "расч", UserExpr1).ResultTree;
     Expr2 = (TablikListNode) new ExprParsing(Keeper, "упр", UserExpr2).ResultTree;
 }
예제 #4
0
        //Семантический разбор поля Inputs
        private void ParseInputs()
        {
            Inputs.Clear();
            InputsList.Clear();
            Vars.Clear();
            var       inputs = (ListNode <InputNode>) new InputsParsing(Keeper, InputsStr).ResultTree;
            TablikVar v      = null;

            foreach (var node in inputs.Nodes)
            {
                var varCode = node.Token.Text;
                if (Inputs.ContainsKey(varCode))
                {
                    Keeper.AddError("Два входа с одинаковыми именами", node);
                }
                else if (varCode.ToLower() == "расчет" || varCode.ToLower() == "calc")
                {
                    Keeper.AddError("Недопустимое имя входа", node);
                }
                switch (node.InputType)
                {
                case InputType.Simple:
                    var dt  = node.TypeNode == null ? DataType.Real : node.TypeNode.Text.ToDataType();
                    var at  = node.SubTypeNode == null ? ArrayType.Single : node.SubTypeNode.Token.Text.ToArrayType();
                    var val = node.ValueNode == null ? null : node.ValueNode.Mean;
                    v = new TablikVar(varCode, new SimpleType(dt, at), val);
                    break;

                case InputType.Param:
                    if (!Module.Params.ContainsKey(node.TypeNode.Text))
                    {
                        Keeper.AddError("Не найден расчетный параметр", node.TypeNode);
                    }
                    else
                    {
                        var par = Module.Params[node.TypeNode.Text];
                        if (node.SubTypeNode == null)
                        {
                            if (!par.IsFun)
                            {
                                Keeper.AddError("Параметр без входов не может быть типом данных входа", node.TypeNode);
                            }
                            else
                            {
                                v = new TablikVar(varCode, par);
                            }
                        }
                        else if (!par.Params.ContainsKey(node.SubTypeNode.Text))
                        {
                            Keeper.AddError("Не найден расчетный подпараметр", node.SubTypeNode);
                        }
                        else
                        {
                            var spar = par.Params[node.SubTypeNode.Text];
                            if (!spar.IsFun)
                            {
                                Keeper.AddError("Подпараметр без входов не может быть типом данных входа", node.SubTypeNode);
                            }
                            else
                            {
                                v = new TablikVar(varCode, spar);
                            }
                        }
                    }
                    break;

                case InputType.Signal:
                    string         scode = node.TypeNode.Text.Substring(1, node.TypeNode.Text.Length - 2);
                    ObjectTypeBase t     = null;
                    foreach (var con in Module.LinkedSources)
                    {
                        if (con.ObjectsTypes.ContainsKey(scode))
                        {
                            if (t == null)
                            {
                                t = con.ObjectsTypes[scode];
                            }
                            else
                            {
                                Keeper.AddError("Одинаковый код типа объекта в двух разных источниках", node);
                            }
                        }
                        else if (con.BaseObjectsTypes.ContainsKey(scode))
                        {
                            if (t == null)
                            {
                                t = con.BaseObjectsTypes[scode];
                            }
                            else
                            {
                                Keeper.AddError("Одинаковый код типа объекта в двух разных источниках", node);
                            }
                        }
                    }
                    if (t != null)
                    {
                        v = new TablikVar(varCode, t)
                        {
                            MetSignals = new SetS(), MetProps = new SetS()
                        }
                    }
                    ;
                    else
                    {
                        Keeper.AddError("Не найден тип объекта или сигнала", node);
                    }
                    break;
                }
                if (v != null)
                {
                    if (Inputs.ContainsKey(v.Code))
                    {
                        Keeper.AddError("Два входа с одинаковыми именами", node);
                    }
                    else
                    {
                        if (v.DefaultValue == null && InputsList.Count > 0 && InputsList[InputsList.Count - 1].DefaultValue != null)
                        {
                            Keeper.AddError("Входы со значениямия по умолчанию должны располагаться в конце списка входов", node);
                        }
                        InputsList.Add(Vars.Add(v.Code, Inputs.Add(v.Code, v)));
                    }
                }
            }
        }
예제 #5
0
        void GenerateInstruction(Instruction i, Function func, Args args, Function old)
        {
            switch (i.Func.Function)
            {
            /*
             * Handle Blocks
             */
            case "_ !block":
                // Handle the block
                Generate(new Function
                {
                    // Keep the name (because of variables).
                    Name = func.Name,
                    // Copy the instructions
                    Instructions = ((IBlock)i).List,
                    // Return nothing
                    Ret = new Return(),
                    // Take no arguments
                    Arguments = new Args
                    {
                        // Because
                        Arguments = new List <Variable>()
                    }
                }, null, func);
                break;

            /*
             * Define new variable from constant
             */
            case "new":
                // Check if it has valid arguments
                if (i.Func.Arguments.Arguments.Count == 1 && i.Func.Arguments.Arguments[0].isConst)
                {
                    byte[] x;
                    // Check if it's a number or string
                    if (i.Func.Arguments.Arguments[0].isNum)
                    {
                        // Number
                        x = BitConverter.GetBytes(int.Parse(i.Func.Arguments.Arguments[0].Name));
                    }
                    else
                    {
                        // String
                        x = Encoding.ASCII.GetBytes(i.Func.Arguments.Arguments[0].Name);
                    }
                    // Define the variable
                    int var = AddVariable(x);

                    // Assign the value to the variable
                    if (i.Op?.Type == "=")
                    {
                        // Check if it's valid
                        if (i.Var?.isConst == false)
                        {
                            // Is it direct? (starts with x)
                            if (i.Var.Name.ToLower().StartsWith("x"))
                            {
                                // Directly move
                                Move(int.Parse(i.Var.Name.ToLower().TrimStart('x')), var);
                            }
                            else
                            {
                                // Define if not already defined
                                if (!Vars.ContainsKey(func.Name + i.Var.Name))
                                {
                                    Vars.Add(func.Name + i.Var.Name, var);
                                }
                                else
                                {
                                    // Change if already defined
                                    Vars[func.Name + i.Var.Name] = var;
                                }
                            }
                        }
                    }
                    // Throw an exception
                }
                else
                {
                    throw new Exception("Invalid arguments in `new`.");
                }
                break;

            /*
             * Multiply two numbers
             */
            case "proc_Mul":
                // Check for valid size of arguments.
                if (i.Func.Arguments.Arguments.Count == 2)
                {
                    Mul(GetArgument(i, func, 0), GetArgument(i, func, 1));
                }
                else
                {
                    throw new Exception("Invalid arguments in `proc_Mul`.");
                }
                break;

            /*
             * Divide two numbers
             */
            case "proc_Div":
                // Check for valid size of arguments.
                if (i.Func.Arguments.Arguments.Count == 2)
                {
                    Div(GetArgument(i, func, 0), GetArgument(i, func, 1));
                }
                else
                {
                    throw new Exception("Invalid arguments in `proc_Div`.");
                }
                break;

            /*
             * Subtract two numbers
             */
            case "proc_Sub":
                // Check for valid size of arguments.
                if (i.Func.Arguments.Arguments.Count == 2)
                {
                    Sub(GetArgument(i, func, 0), GetArgument(i, func, 1));
                }
                else
                {
                    throw new Exception("Invalid arguments in `proc_Sub`.");
                }
                break;

            /*
             * Add two numbers
             */
            case "proc_Add":
                // Check for valid size of arguments.
                if (i.Func.Arguments.Arguments.Count == 2)
                {
                    Add(GetArgument(i, func, 0), GetArgument(i, func, 1));
                }
                else
                {
                    // Thow an exception
                    throw new Exception("Invalid arguments in `proc_Add`.");
                }
                break;

            /*
             * Create a label
             */
            case "label":
                if (i.Func.Arguments.Arguments.Count == 1 && !i.Func.Arguments.Arguments[0].isConst)
                {
                    int var = AddVariable(BitConverter.GetBytes(++LabelID));
                    Label(var);
                    if (i.Func.Arguments.Arguments[0].Name.ToLower().StartsWith("x"))
                    {
                        // Directly move
                        Move(int.Parse(i.Func.Arguments.Arguments[0].Name.ToLower().TrimStart('x')), var);
                    }
                    else
                    {
                        // Define if not already defined
                        if (!Vars.ContainsKey(func.Name + i.Func.Arguments.Arguments[0].Name))
                        {
                            Vars.Add(func.Name + i.Func.Arguments.Arguments[0].Name, var);
                        }
                        else
                        {
                            // Change if already defined
                            Vars[func.Name + i.Func.Arguments.Arguments[0].Name] = var;
                        }
                    }
                }
                else
                {
                    throw new Exception("Invalid arguments in `label`.");
                }
                break;

            case "goto":
                if (i.Func.Arguments.Arguments.Count == 2 && !i.Func.Arguments.Arguments[0].isConst && !i.Func.Arguments.Arguments[1].isConst)
                {
                    // 30 Lines less xD
                    Goto(GetArgument(i, func, 0), GetArgument(i, func, 1));
                }
                else
                {
                    throw new Exception("Invalid arguments in `goto`.");
                }
                break;

            /*
             * Clear Value
             */
            case "clear":
                // Check for the arguments count.
                if (i.Func.Arguments.Arguments.Count == 1)
                {
                    // Clear the variable.
                    ClearVar(GetArgument(i, func, 0));
                }
                else
                {
                    throw new Exception("Invalid arguments in `clear`.");
                }
                break;

            /*
             * Move Functions
             */
            case "address":
                if (i.Func.Arguments.Arguments.Count == 2)
                {
                    Move(GetArgument(i, func, 0), GetArgument(i, func, 1));
                }
                break;

            case "content":
                if (i.Func.Arguments.Arguments.Count == 2)
                {
                    MoveC(GetArgument(i, func, 0), GetArgument(i, func, 1));
                }
                break;

            /*
             * Compiler Functions
             */
            case "set_label":
                if (i.Func.Arguments.Arguments.Count == 1 && i.Func.Arguments.Arguments[0].isNum)
                {
                    CurrentVar = int.Parse(16 + i.Func.Arguments.Arguments[0].Name);
                }
                else
                {
                    throw new Exception("Invalid arguments in `set_label`.");
                }
                break;

            case "set_variable":
                if (i.Func.Arguments.Arguments.Count == 1 && i.Func.Arguments.Arguments[0].isNum)
                {
                    LabelID = int.Parse(i.Func.Arguments.Arguments[0].Name);
                }
                else
                {
                    throw new Exception("Invalid arguments `in set_variable`.");
                }
                break;

            /*
             * Get value
             */
            case "get":
                // Check for the arguments count
                if (i.Func.Arguments.Arguments.Count == 1)
                {
                    int var = GetArgument(i, func, 0);

                    // Check if sould assign
                    if (i.Op?.Type == "=")
                    {
                        // Check if the variable is valid
                        if (i.Var?.isConst == false)
                        {
                            // If it starts with "x", directly put into the variable
                            if (i.Var.Name.ToLower().StartsWith("x"))
                            {
                                // Move.
                                Move(int.Parse(i.Var.Name.ToLower().TrimStart('x')), var);
                            }
                            else
                            {
                                // If the variable isn't defined, define it.
                                if (!Vars.ContainsKey(func.Name + i.Var.Name))
                                {
                                    Vars.Add(func.Name + i.Var.Name, var);
                                }
                                else
                                {
                                    // Change the variable
                                    Vars[func.Name + i.Var.Name] = var;
                                }
                            }
                        }
                    }
                }
                else
                {
                    // Throw the exception
                    throw new Exception("Incorrect amount of arguments in `get`.");
                }
                break;

            /*
             * The Is function.
             */
            case "is":
                // Default size is 4
                int mSize = 4;
                // If there is a second argument
                if (i.Func.Arguments.Arguments.Count == 2)
                {
                    // And it's a number
                    if (i.Func.Arguments.Arguments[1].isNum)
                    {
                        // Set the size to that number
                        mSize = int.Parse(i.Func.Arguments.Arguments[1].Name);
                    }
                    else
                    {
                        // If not, thow an exception
                        throw new Exception("Invalid argument in `is`.");
                    }
                }
                // Select the old variable
                int oldVar = 0;
                // Create the new variable
                int newVar = AddVariable(new byte[mSize]);
                // Get your sources
                if (i.Func.Arguments.Arguments[0].isConst == false)
                {
                    // Check if it is an address
                    if (i.Func.Arguments.Arguments[0].Name.ToLower().StartsWith("x"))
                    {
                        // Set the oldVar address to that address.
                        oldVar = int.Parse(i.Func.Arguments.Arguments[0].Name.ToLower().TrimStart('x'));
                    }
                    else
                    {
                        // Find a predefined variable.
                        oldVar = Vars[func.Name + i.Func.Arguments.Arguments[0].Name];
                    }
                }
                else
                {
                    throw new Exception("Invalid argument in `is`.");
                }
                // Where to assign
                if (i.Op?.Type == "=")
                {
                    // Check if the variable is valid
                    if (i.Var?.isConst == false)
                    {
                        // If it starts with "x", directly put into the variable
                        if (i.Var.Name.ToLower().StartsWith("x"))
                        {
                            // Move.
                            newVar = int.Parse(i.Var.Name.ToLower().TrimStart('x'));
                        }
                        else
                        {
                            // If the variable isn't defined, define it.
                            if (!Vars.ContainsKey(func.Name + i.Var.Name))
                            {
                                Vars.Add(func.Name + i.Var.Name, newVar);
                            }
                            else
                            {
                                // Change the variable
                                Vars[func.Name + i.Var.Name] = newVar;
                            }
                        }
                    }
                }
                MoveC(newVar, oldVar);
                break;

            /*
             * Interrupt
             */
            case "int":
                // Check if the arguments are correct & interrupt
                if (i.Func.Arguments.Arguments.Count == 1 && i.Func.Arguments.Arguments[0].isNum)
                {
                    int x = int.Parse(i.Func.Arguments.Arguments[0].Name);
                    // Interrupt
                    Interrupt((byte)x);
                }
                else
                {
                    // Thow the exception
                    throw new Exception("Interrupt can only accept bytes as arguments.");
                }
                break;

            /*
             * Execute Function
             */
            default:
                // Find the function
                Function f = find(i.Func.Namespace, i.Func.Function);
                // Set the function name to something unique to the namespace
                f.Name = i.Func.Namespace + " " + i.Func.Function + " ";
                // Generate the function, and return.
                Generate(f, i.Func.Arguments, func);
                // Return if it's a variable
                if (f.Ret?.Type == TokenType.Variable)
                {
                    int var;
                    // Check if should return address.
                    if (f.Ret.Value.ToLower().StartsWith("x"))
                    {
                        // Get direct address.
                        var = int.Parse(f.Ret.Value.ToLower().TrimStart('x'));
                    }
                    else
                    {
                        // Get variable.
                        var = Vars[f.Name + f.Ret.Value];
                    }
                    // Check if should assign
                    if (i.Op?.Type == "=")
                    {
                        if (i.Var?.isConst == false)
                        {
                            // Check if it's a direct address
                            if (i.Var.Name.ToLower().StartsWith("x"))
                            {
                                // Move the data**
                                MoveC(int.Parse(i.Var.Name.ToLower().TrimStart('x')), var);
                            }
                            else
                            {
                                // Define the variable if it's not already defined
                                if (!Vars.ContainsKey(func.Name + i.Var.Name))
                                {
                                    // Define Variable
                                    Vars.Add(func.Name + i.Var.Name, var);
                                }
                                else
                                {
                                    // Change Variable
                                    Vars[func.Name + i.Var.Name] = var;
                                }
                            }
                        }
                    }
                }
                else
                {
                    // Make sure that it's an invalid return and not a void
                    if (f.Ret?.Type == TokenType.Number ||
                        f.Ret.Type == TokenType.String)
                    {
                        throw new Exception("Functions can only return variables!");
                    }
                }
                // Collect the garbadge (if not in block)
                if ((old != null) && (old.Name == func.Name))
                {
                    CollectGarbadge(f.Name);
                }
                break;
            }
        }
예제 #6
0
 public void AddVar(VarDeclarationElement elem)
 {
     Vars.Add(elem);
     AddChild(elem);
 }
예제 #7
0
파일: Env.cs 프로젝트: djlw78/abanu
 public static void Set(string name, string value)
 {
     Vars.Remove(name);
     Vars.Add(name, value);
 }
예제 #8
0
 public void CreateVar(string varName, string varType, object varValue = null) => Vars.Add(varName, new Var(varType, varValue));
예제 #9
0
        public Form1()
        {
            InitializeComponent();

            CS.ConsoleLog   += new CS.ConsoleLogEventHandler(CS_ConsoleLog);
            CS.EnableLogging = true;

            Listener           = new Wo1fSocketListener((int)numericUpDown1.Value);
            Listener.OnAccept += new Wo1fSocketListener.SocketAcceptedEventHandler(Listener_OnAccept);

            //Wo1f_Chat_Client.Form1 F = new Wo1f_Chat_Client.Form1();
            //F.Show();

            //Listener.Settings.IsServer = true;
            //Listener.Listen();
            Vars.Add("Servername", "Server");
            Vars.Add("motd", "This is the MOTD.");
            Clients.DB.DBType = Wo1f_Framework.SQL.DatabaseType.MySQL;
restart:
            int tries = 0;

            if (Clients.DB.Connect("127.0.0.1", "Wo1fChat", "root", "Kiba10"))
            {
                this.TryChat("[System] Connected to SQL Database");
                bool resettables = false;
                if (resettables)
                {
                    Clients.DB.SendCmd("DROP TABLE clients");
                }
                if (!Clients.DB.CheckExists("clients"))
                {
                    this.TryChat("[System] Table not found.  Generating...");
                    //create tables
                    Clients.DB.SendCmd("CREATE TABLE clients (id INT NOT NULL AUTO_INCREMENT, " +
                                       "name VARCHAR(32), pass VARCHAR(32), hash VARCHAR(64), info1 VARCHAR(255), info2 VARCHAR(255), primary key(id));");
                    if (Clients.DB.CheckExists("clients"))
                    {
                        this.TryChat("[System] Table created.");
                    }
                    else
                    {
                        this.TryChat("[System] Table not created for some reason.");
                    }
                }
                else
                {
                    DataTable         DT  = Clients.DB.QueryCmd("SELECT COUNT(*) FROM clients");
                    DataRowCollection DR  = DT.Rows;
                    object[]          ret = DR[0].ItemArray;
                    var i = ret[0];

                    TryChat("Table loaded successfully. Found " + i + " entries.");
                }
                //ready to load info if needed
            }
            else if (Clients.DB.Connect("127.0.0.1", "", "root", "Kiba10"))
            {
                TryChat("Database does not exist.  Attempting to generate");
                //needs to create DB
                Clients.DB.SendCmd("CREATE DATABASE Wo1fChat");

                if (tries <= 1)
                {
                    tries++;
                    goto restart;
                }
                TryChat("Database generation retry amount exceeded.");
            }
            else
            {
                this.TryChat("[System] Database connection failed.");
            }

            //packets

            Cmd.Register("refresh", new CommandDelegate(OnRefreshRequest));
            Cmd.Register("msg", new CommandDelegate(OnMsg));
            Cmd.Register("reg", new CommandDelegate(OnRegister));
            Cmd.Register("register", new CommandDelegate(OnRegister));
            Cmd.Register("name", new CommandDelegate(OnNameRequest));
            Cmd.Register("login", new CommandDelegate(OnLoginRequest));
        }
예제 #10
0
        //calc - CalcParam с формулой для расчета, inputs - список входных значений
        //owner - владелец, caller - параметр, из которого вызывается
        public CalcParamRun(CalcParam calc, CalcValue[] inputs, CalcParamRun owner, CalcParamRun caller)
        {
            CalcParam = calc;
            Owner     = owner;
            Caller    = caller;
            if (inputs == null || inputs.Length == 0)
            {
                Owner.Methods.Add(calc.Code, this);
                if (calc.IsNotObject)
                {
                    calc.RunParam = this;
                }
            }

            StackDepth = caller == null ? 1 : caller.StackDepth + 1;
            if (StackDepth > 500)
            {
                CalcValue = new CalcValue(new SingleValue(new Moment(DataType.Value, new ErrorCalc("Переполнение стека", CalcParam.FullCode))));
                return;
            }

            foreach (var k in calc.Vars.Keys)
            {
                Vars.Add(k, new VarRun(calc.Vars[k]));
            }

            if (inputs != null)
            {
                for (int i = 0; i < inputs.Length; i++)
                {
                    VarRun v = Vars[calc.Inputs[i]];
                    v.CalcValue = inputs[i];
                    Inputs.Add(v);
                }
            }

            ThreadCalc = CalcParam.Project.ThreadCalc;
            //Сразу производится расчет
            Calculate();

            //Добавление ошибок расчета в проект
            if (CalcParam.Inputs.Count == 0)
            {
                var errors = CalcParam.Project.CalcErrors;
                if (CalcValue.Type != CalcValueType.Single)
                {
                    if (CalcValue.Error != null)
                    {
                        errors.Add(CalcValue.Error);
                    }
                }
                else
                {
                    var sv = CalcValue.SingleValue;
                    if (sv.Moment != null && sv.Moment.Error != null)
                    {
                        errors.Add(sv.Moment.Error);
                    }
                    if (sv.Moments != null)
                    {
                        foreach (var m in sv.Moments)
                        {
                            if (m.Error != null)
                            {
                                errors.Add(m.Error);
                                break;
                            }
                        }
                    }
                    if (sv.Segments != null)
                    {
                        foreach (var seg in sv.Segments)
                        {
                            if (seg.Error != null)
                            {
                                errors.Add(seg.Error);
                                break;
                            }
                        }
                    }
                }
            }
        }
예제 #11
0
        public static Vars Parse(
            string[] lines,
            bool trimWhitespace        = true,
            bool isEmbeddedHashComment = true,
            bool unescapeQuotedValues  = true,
            bool parseVariables        = true
            )
        {
            Vars vars = new Vars();

            for (int i = 0; i < lines.Length; i++)
            {
                string line = lines[i];

                // skip comments
                if (IsComment(line))
                {
                    continue;
                }

                if (isEmbeddedHashComment)
                {
                    line = RemoveInlineComment(line);
                }

                line = RemoveExportKeyword(line);

                string[] keyValuePair = line.Split(new char[] { '=' }, 2);

                // skip malformed lines
                if (keyValuePair.Length != 2)
                {
                    continue;
                }

                if (trimWhitespace)
                {
                    keyValuePair[0] = keyValuePair[0].Trim();
                    keyValuePair[1] = keyValuePair[1].Trim();
                }

                if (unescapeQuotedValues && IsQuoted(keyValuePair[1]))
                {
                    keyValuePair[1] = Unescape(
                        keyValuePair[1].Substring(1, keyValuePair[1].Length - 2)
                        );
                }

                if (parseVariables)
                {
                    keyValuePair[1] = ParseVariables(vars, keyValuePair[1]);
                }

                vars.Add(
                    keyValuePair[0],
                    keyValuePair[1]
                    );
            }

            return(vars);
        }