赞
踩
图形化编程在很多领域都应用很多,比如儿童编程、硬件控制,目前最火的两个引擎肯定应该是scratch和google blocky,很多图形化编程软件都是基于这两个引擎,比如国内编程猫使用了scratch,makecode使用了google blocky。
使用其他引擎必然会收到很大的限制,不如要实现比如局部变量等基本功能时候,使用scratch就没法实现了,而且scratch和blocky都是基于javascript,如果要控制某些硬件,就要做很多javascript和其他硬件的通信模块,不是很方便,所以我们也许要自己写一个图形化编程工具。我工作一直用图形化工具控制硬件,但是老外的软件做的很一般,有天突然想是否自己也能写一个图形化编程工具,就花了点时间,用C#和WPF写了一个图形化工具,代码已经放在github下开源
代码地址 https://github.com/weihuajiang/WPF-Blocky
也在代码中实现了一个代码编辑器,和scratch一样的编辑器
图形化程序也是程序,要显示和运行,我们必须要知道程序语法树(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"
}
}
执行顺序是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; } } }
我们使用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(); } }
我们下边以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); } }
在githup上开源了编辑器的代码,大家可以下载看看,同时这个编辑器放在了微软商店里,链接是Visual Code Editor,如果想用微软商店直接安装,可以点击下边连接,
ms-windows-store://pdp/?productid=9NG2QVSXT34H
里边除了基本的语法功能,增加了更多类库和多语言等功能,比如python turtle一样的画板、翻译、语音、数学、数据结构等
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。