当前位置:   article > 正文

C#和WPF实现图形化编程_wpf 图形化编程

wpf 图形化编程

C#和WPF实现图形化编程

图形化编程在很多领域都应用很多,比如儿童编程、硬件控制,目前最火的两个引擎肯定应该是scratch和google blocky,很多图形化编程软件都是基于这两个引擎,比如国内编程猫使用了scratch,makecode使用了google blocky。

使用其他引擎必然会收到很大的限制,不如要实现比如局部变量等基本功能时候,使用scratch就没法实现了,而且scratch和blocky都是基于javascript,如果要控制某些硬件,就要做很多javascript和其他硬件的通信模块,不是很方便,所以我们也许要自己写一个图形化编程工具。我工作一直用图形化工具控制硬件,但是老外的软件做的很一般,有天突然想是否自己也能写一个图形化编程工具,就花了点时间,用C#和WPF写了一个图形化工具,代码已经放在github下开源

代码地址 https://github.com/weihuajiang/WPF-Blocky

也在代码中实现了一个代码编辑器,和scratch一样的编辑器
代码编辑器
Scratch编辑器

程序语法树AST的实现

图形化程序也是程序,要显示和运行,我们必须要知道程序语法树(abstract syntax tree)的概念。语法树可以分为表达式Expression、语句Statement和声明Declaration。比如简单的一个语句a+b,就是一个BinaryExpression,而a+b;虽然多了分号,不过就编程了一个Statement语句,这个是个ExpressionStatement,其中的表达式就是BinaryExpression。更多AST的信
息大家可以看esprima解释的很详细。在我们程序中,我们定义了AST基类Node和三个基本的语法树结构Expression、Statement和Decaration(主要就是函数定义),与之对应基本控件也就只有三个控件,Expression控件、Statement控件和Function控件。WPF的数据绑定,让控件编写简化了很多。

程序的运行

程序的运行,简单来说就是各个语法树节点的运行,比如a+5;可以转换成以下结构:

            "type": "ExpressionStatement",
            "expression": {
                "type": "BinaryExpression",
                "operator": "+",
                "left": {
                    "type": "Identifier",
                    "name": "a"
                },
                "right": {
                    "type": "Literal",
                    "value": 5,
                    "raw": "5"
                }
            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

执行顺序是ExpressionStatement->BinaryExpression->left, right,返回left+right,left是Identifier是变量,需要从运行环境获得变量的值,right是Literal是整数5,这样就能获得运行的结果。

程序跳转的实现

break、continue和return,还有异常都会导致程序运行顺序发生跳转,我们通过程序返回值类型来实现程序的跳转。返回值总共有五种类型,分别对应五种可能情况,如果执行过程中出现异常,则应返回Exception,如果正确执行返回Value类型,循环会处理continue和break,函数处理return,如果执行过程中返回非Value,则应该把改返回类型返回,这样就解决了程序跳转的问题。

    public enum CompletionType
    {
        Value,
        Continue,
        Break,
        Return,
        Exception
    }
    public class Completion
    {
        public static Completion Void = new Completion();
        public Node Location { get; internal set; } = null;
        public CompletionType Type { get; internal set; } = CompletionType.Value;
        public object ReturnValue { get; internal set; } = null;

        public bool IsValue
        {
            get
            {
                return Type == CompletionType.Value;
            }
        }
        public bool IsException
        {
            get
            {
                return Type == CompletionType.Exception;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

运行环境的实现

我们使用Dictionary来保存运行运行环境的变量值,当前变量就保存在当前的Dictionary中,运行到一个位置,比如需要一个新的运行环境时,就生成一个新的环境,这样在这个环境中定义的变量在parent环境就无法访问,也就实现了局部变量的操作,同时当前环境可以访问前边所有parent环境的数据,也就是访问其他作用域的变量。

    public class ExecutionEnvironment
    {
        ExecutionEnvironment _parent=null;
        Dictionary<string, object> currentVariables = new Dictionary<string, object>();
        public ExecutionEnvironment()
        {
            _parent = null;
        }
        public ExecutionEnvironment(ExecutionEnvironment parent)
        {
            _parent = parent;
        }
        public bool HasValue(string variable)
        {
            if (currentVariables.ContainsKey(variable))
                return true;
            if (_parent != null)
                return _parent.HasValue(variable);
            return false;
        }
        public void RegisterValue(string variable, object value)
        {
            if (currentVariables.ContainsKey(variable))
                throw new Exception(Variable was defined already");
            currentVariables.Add(variable, value);
        }
        public object GetValue(string variable)
        {
            if (currentVariables.ContainsKey(variable))
            {
                return currentVariables[variable];
            }
            if (_parent != null)
                return _parent.GetValue(variable);
            throw new KeyNotFoundException();
        }
        public void SetValue(string variable, object value)
        {
            if (currentVariables.ContainsKey(variable))
            {
                currentVariables[variable] = value;
                return;
            }
            else if (_parent != null)
            {
                _parent.SetValue(variable, value);
                return;
            }
            else
                throw new KeyNotFoundException();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

具体实现的例子

我们下边以if语句为例,看看具体如何实现语句的运行,if语句包含三部分,条件判断表达式Test,true时候运行的Consequent语句,false时候运行的Alternate语句,运行时候,我们先执行Test.ExecuteImpl语句,如果为true,则运行Consequent,false时候运行Alternate,不过在Consequent和Alternate运行时候,都产生了一个新的变量作用域,我们需要新建一个运行环境。


    public class IfStatement : Statement
    {
        public IfStatement()
        {
            Consequent = new BlockStatement();
        }
        public Expression Test { get; set; }
        public BlockStatement Consequent {get; set;}
        public BlockStatement Alternate {get; set;}
        protected override Completion ExecuteImpl(ExecutionEnvironment enviroment)
        {
            if (Test == null)
                return Completion.Void;
            var t = Test.Execute(enviroment);
            if (!t.Type.IsValue)
                return t;
            if (t.ReturnValue is bool)
            {
                if ((bool)t.ReturnValue)
                {
                    if (Consequent == null)
                        return Completion.Void;
                    ExecutionEnvironment current = new ExecutionEnvironment(enviroment);
                    return Consequent.Execute(current);
                }
                else
                {
                    if (Alternate == null)
                        return Completion.Void;
                    ExecutionEnvironment current = new ExecutionEnvironment(enviroment);
                    return Alternate.Execute(current);
                }
            }
            else
                return Completion.Exception("Not a boolean value", Test);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

结语

githup上开源了编辑器的代码,大家可以下载看看,同时这个编辑器放在了微软商店里,链接是Visual Code Editor,如果想用微软商店直接安装,可以点击下边连接,
ms-windows-store://pdp/?productid=9NG2QVSXT34H
里边除了基本的语法功能,增加了更多类库和多语言等功能,比如python turtle一样的画板、翻译、语音、数学、数据结构等
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/169903
推荐阅读
相关标签
  

闽ICP备14008679号