1 Star 2 Fork 0

lzt841 / 自定义脚本解释器

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

自定义脚本解释器

介绍

使用java实现的一个自定义脚本语言解释器,支持自定义关键词,自定义命令,自定义操作运算,自定义基本类型.


    global :指全局,可以作用在变量和方法上,且其他脚本都共享
		例如:  global.list[0]={}
			  global.printf('xxx')
		self:和global一样,只不过作用域不一样,只针对当前脚本内的方法和变量
		 例子: self.map['key'][0]=1
			  self.main()
		循环:
		for循环:(for的()括号不能去掉)

                for(xxx;xxx;xxx){//标准for
		}
		  for(xxx;xxx;)
		  	xxxx

		2.for(x:map){//遍历对象
			
		}
		  for(x:map)
		  	xxxxx
		3.for(x:list){//遍历list
			
		}
		  for(x:list)
		     xxxxxx
		while循环:
		1.while(xxx){
			
		}
		2.while xxxx{
			
		}
		3. while xxxx  xxxxx
		
		do while循环:
		1.do{
			
		}while(xxxx)
		{}和()可以去掉
		
		if判断:
		1.	if xxxx
				xxxxxxx
		2.  if xxxx
				Xxxxx
			else
				xxxxx
		3.  if xxx
				xxxxx
			else if xxxx
				xxxxx
			else if xx
			.....
		{}和()可以去掉
		
		三目运算:
			xxxx?aaaaa:bbbb
		
		操作运算:
		+-*!%/&>><<==...等等都支持
		内置有少许函数,可在com.lzt841.script.language.ls.function.instances下查看具体函数
		可以自定义关键字,自定义操作运算,自定义函数,自定义基本类型,自定义命令;

使用说明

//在项目build.gradle添加
repositories {
		mavenCentral()
		maven { url 'https://jitpack.io' }
		}
//引入依赖
dependencies {
		implementation 'com.gitee.lzt841:custom-script:0.1.0'
		}
  1. 实现自定义执行命令,自定义关键词,以下示例实现#命令处理,实现打印功能
    将方法定义关键词修改为中文“方法“,修改后对已加载的脚本无影响,大 部分关键词都可以修改,只要注意不建议修改token,还有不要词重复重叠就行,一个词只有一个token

    	String scriptName="test";
      	KeyWordConfig keyWordConfig=new KeyWordConfig();
    	OperatorSymbolConfig operatorSymbolConfig=new OperatorSymbolConfig();
    	//将方法定义关键词修改为中文“方法“,修改后对已加载的脚本无影响,大部分关键词都可以修改,只要注意不建议修改token,还有不要词重复重叠就行
    	keyWordConfig.FUNCTION_KEYWORD.keyword="方法";
    	//运算符和关键词一样,可以修改,token不建议改
    	operatorSymbolConfig.EQUAL.operator="等于";

    测试用例代码脚本

    	String code="方法 main(){\n"
    					+"#'开始自定义命令打印了' \n"
    					+"a=true \n"
    					+"if (a 等于 true)\n"
    					+	"#'打印成功' \n"
    					+"else "
    					+"#'打印错误'\n"
    					+"a='我是变量\n'\n"
    					+"//测试打印变量\n"
    					+"#a   \n"
    					+"//测试打印公式\n"
    					+"#a+',我是拼接字符串,'+(12+32)/4/6.0f\n"
    					+"}\n";

    创建环境,注入操作指令符词分析器

    	LsEnvironment scripts=new LsEnvironment(keyWordConfig,operatorSymbolConfig);
    	//第一步:获取词法分析器,注入一个#符合的词;给这个#一个token,这个token要唯一,随便取,取200以外好,要保证唯一就行,例子里就2000好了
    	LsLexicalAnalysis analysis= scripts.getLexicalHandler();
    	final int token=2000;
    	//添加词处理程序进去
    	analysis.addCharHandler(new LsSymbolCharHandler(new Keyword("#",token)));

    写一个注入处理这个#词的命令的类

    	ExecuteHandler execute=new ExecuteHandler(){
    		//这个方法是做处理命令的判断,返回true代表能处理,我们只处理#的命令,所以当命令为#时返回true就好
    		@Override
    		public boolean canExecute(LsExecuteContext context, List<CodeNode> codeNodes, int i)
    		{
    			//拿到当前位置的节点(i是指当前节点位置)
    			CodeNode node=codeNodes.get(i);
    			//取出节点的token
    			int nodeToken=node.getFirstWordToken();
    			//这个token和我们刚才定义的#命令的token一样就返回true就好了
    			return nodeToken==token;
    		}
    		//这个方法是具体的执行操作方法
    		@Override
    		public int execute(LsExecuteContext executeHandle, ScriptFile scriptFile, Map<String, Object> variableMap, List<CodeNode> codeNodes, int i, Result funResult)
    		{
    			//已知道i是#节点的位置,我们要拿到#后面的结果,可用executeHandle的getResult方法得到后面的计算结果,
    			//如果不想要计算结果,可以用getValue,只获取一个值,不对之后是否有运算符都忽略掉了
    			//创建一个空结果对象去接收结果,方法返回的是指针往下走的位置,一般都是跟着指针走的(逻辑分支才根据逻辑走),所以直接让i跳到指针位置
    			Result result=new Result();
    			i=executeHandle.getResult(scriptFile,codeNodes,variableMap,i+1,result);
    			//拿到结果后怎么办?#命令是用来打印的,那当然是用起来
    			System.out.println(result.result);
    			
    			//完成,将新的指针位置返回给处理程序
    			return i;
    		}
    		//跳过指令
    		@Override
    		public int skipExecute(LsExecuteContext executeHandle, List<CodeNode> codeNodes, int i) {
    			//指令需要从后面拿一个结果,所以返回跳过下一个结果的指针
    			return executeHandle.skipResult(codeNodes,i+1);
    		}
    	};

    最后一步

    	//将命令处理程序放到处理程序序列的前面,优先处理
    	 scripts.getExecuteContext().getExecuteHandlers().add(0,execute);
    	//加载脚本
    	scripts.loadScriptFile(scriptName,code);
    	//运行脚本方法
    	Object result= scripts.execute(scriptName,"main",new ArrayList());
    	
  2. 自定义全局方法,变量操作方法
    测试用例代码脚本

    //测试用例代码脚本
    String code=
    		"function main(){"
    		+"a=123*23 \n"
    		+"b=45\n"
    		+"//用self关键词强行指定方法为脚本方法\n"
    		+"//self.min(a,b)\n"
     	+"if (min(a,b)==a){\n"
     	+"printf('a是最小值\n') \n"
     	+"}else{ "
     	+"printf('b是最小值\n')"
     	+"\n}"
     	+"x={}"
     	+"print('x的类型:'+x.type())"
     +"}";`
    LsEnvironment scripts=new LsEnvironment();

    注册全局方法min,求最小值

    //注册全局方法min,求最小值
    scripts.registerGlobalFunction("min", new FunctionHandler(){
    @Override
    public Object execute(List parameters)
    {
    	if(parameters.size()==0)return 0;
    	if(parameters.size()==1)return parameters.get(0);
    	return Math.min(CalculateUtils.toInteger(parameters.get(0)),CalculateUtils.toInteger(parameters.get(1)));
    }
    });

    注册变量操作方法type(),返回变量的类型字符串

    //注册变量操作方法type(),返回变量的类型字符串
    scripts.registerVariableFunction("type", new VariableFunction(){
    
    	@Override
    	public Object execute(Object variable, List parameters)
    	{
    		if(Map.class.isInstance(variable)){
    			return "map";
    		}else if(List.class.isInstance(variable)){
    			return "list";
    		}else if(Integer.class.isInstance(variable)){
    			return "int";
    		}else if(Float.class.isInstance(variable)){
    			return "float";
    		}else if(Boolean.class.isInstance(variable)){	
    		return "boolean";
    		}else if(CharSequence.class.isInstance(variable)){
    			return "string";
    		}else{
    			return "other";
    		}
    	}
    });
    //加载脚本
    scripts.loadScriptFile(scriptName,code);
    //运行脚本方法
    Object result= scripts.execute(scriptName,"main",new ArrayList());
  3. 添加变量基本类型,增加一个char类型

    	//测试用例代码脚本
    	String code=
    		"function main(){"
    		+"a=123*23 \n"
    		+"b=45\n"
    	
    		+"x='a'"
    		+"y='字符串'"
    		+"print('x的类型:'+x.type()+'\n')"
    		+"print('y的类型:'+y.type()+'\n')"
    		+"}";
    LsEnvironment scripts=new LsEnvironment();

    实例化值处理程序接口

    //实例化值操作接口
    	ValueHandler charValueHandler=new ValueHandler(){
    		@Override
    		public boolean canGetValue(LsExecuteContext context, List<CodeNode> codeNodes, int start)
    		{
    			// 返回能否处理这个节点为值,我们要处理''的节点,就在节点token为'的token时返回true
    			CodeNode node= codeNodes.get(start);
    			int token=node.getFirstWordToken();
    			//token为‘的token时返回true
    			return token==Constant.STRING1_START_KEYWORD.token;
    		}
    
    		@Override
    		public Value getValue(LsExecuteContext context, ScriptFile scriptFile, Map<String, Object> variableMap, List<CodeNode> codeNodes, int start)
    		{
    			//在这里返回节点的值,我们让''的长度为1时就返回char类型,长度不为1时就反会字符串类型
    			Value value=new Value();
    			value.start=start;//这个指针要设置上去
    			CodeNode node=codeNodes.get(start);
    			//拿到脚本节点的原文本
    			String text=node.getFirstWordText();
    			if(text.length()!=1){//长度不为,那就是字符串
    				value.value=text;
    			}else{
    				//长度为1时就返回一个字符
    				value.value=text.charAt(0);
    			}
    			return value;
    		}
    
    		@Override
    		public int skipValue(LsExecuteContext context, List<CodeNode> codeNodes, int start)
    		{
    			// 返回处理的新的指针,没有处理,直接返回原位置
    			return start;
    		}
    	};

    注入到处理环境中去

    //将我们的值处理程序放到处理程序序列的前面,优先处理
    	scripts.getExecuteContext().getValueHandlers().add(0,charValueHandler);

    套用Test2示例的查看类型方法,查看新的类型是否成功

    	//套用Test2示例
    	//注册变量操作方法type(),返回变量的类型字符串
    	scripts.registerVariableFunction("type", new VariableFunction(){
    			@Override
    			public Object execute(Object variable, List parameters)
    			{
    				if(Character.class.isInstance(variable)){
    					return "char";
    				}else if(Map.class.isInstance(variable)){
    					return "map";
    				}else if(List.class.isInstance(variable)){
    					return "list";
    				}else if(Integer.class.isInstance(variable)){
    					return "int";
    				}else if(Float.class.isInstance(variable)){
    					return "float";
    				}else if(Boolean.class.isInstance(variable)){	
    					return "boolean";
    				}else if(CharSequence.class.isInstance(variable)){
    					return "string";
    				}else{
    					return "other";
    				}
    			}
    		});
    	  //加载脚本
    	  scripts.loadScriptFile(scriptName,code);
    	  //运行脚本方法
    	   Object result= scripts.execute(scriptName,"main",new ArrayList());
  4. 实现自定义操作运算
    #描述:
    自定义操作运算符,增加一个复合运算符,实现 自定义‘$‘符,达到 10$2得到14的结果,就是左边加上两倍的右边的效果
    示例

      1. 123$23=169 >>> 123+2*23=169
      1. 45$4=53 >>> 45+2*4=53
    //测试用例代码脚本
    	String code=
    		"function main(){"
    		+"a=123$23 \n"
    		+"b=45$4\n"
    		+"printf('123$23='+a+'\n')"
    		+"printf('45$4='+b+'\n')"
    		+"}";
     //第一步:获取词法分析器,注入一个$符合的词;给这个$一个token,这个token要唯一,随便取,取200以外好,要保证唯一就行,例子里就2010好了
      LsLexicalAnalysis analysis= scripts.getLexicalHandler();
      final int token=2010;
      //添加词处理程序进去
      analysis.addCharHandler(new LsSymbolCharHandler(new Keyword("$",token)));

    实例化运算接口

    	CalculateResult.OperatorValue operatorValue=new CalculateResult.OperatorValue(){
    
    		@Override
    		public int getOperatorValue(LsExecuteContext context, ScriptFile scriptFile, Map<String, Object> variableMap, List<CodeNode> codeNodes, int start, List<Value> operatorValuesOut, int operatorToken, OperatorHandler operatorHandler)
    		{
    			//start为当前运算符的位置,要拿到下一位值,所以start+1
    			Value value= context.getValue(scriptFile,variableMap,codeNodes,start+1);
    			//将运算符token设置到值缓存里
    			value.operatorToken=operatorToken;
    			//将value放到预运算队列上
    			operatorValuesOut.add(value);
    			//返回值所在的指针,好让程序继续往下走
    			
    			return value.start;
    		}
    		
    
    		@Override
    		public boolean canOperator(int operatorToken)
    		{
    			// 当运算符token和自定义token相同时运算
    			return token==operatorToken;
    		}
    
    		
    		@Override
    		public int getOperatorLevel(int operatorToken)
    		{
    			// 得到运算符的优先级,我们让其和*/同级
    			return OperatorConstant.MULTIPLY.operatorLevel;
    		}
    
    		@Override
    		public int skipOperatorValue(LsExecuteContext context, List<CodeNode> codeNodes, int start, int operatorToken)
    		{
    			// 跳过运算,我们这个运算只对下一位运算,那就跳到下一位值的地方
    			return context.skipValue(codeNodes,start+1);
    		}
    
    		@Override
    		public Object operator(Value left, int operatorToken, Value right)
    		{
    			
    			//开始运算,返回结果,我们实现左边加上两倍的右边的效果
    			int value= CalculateUtils.toInteger(left.value)+2*CalculateUtils.toInteger(right.value);
    			return value;
    		}
    	};

    将运算处理值程序放入到运算程序中,运行,完成!

    //将运算处理值程序放入到运算程序中
    	scripts.getExecuteContext().getResultHandler().getOperatorValues().add(operatorValue);
    	
    	//加载脚本
    	scripts.loadScriptFile(scriptName,code);
    
    
    	//运行脚本方法
    	Object result= scripts.execute(scriptName,"main",new ArrayList());

空文件

简介

使用java实现的一个自定义脚本语言解释器,支持自定义关键词,自定义命令,自定义操作运算,自定义基本类型. 展开 收起
Java 等 2 种语言
取消

发行版 (5)

全部

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/lzt841/custom-script.git
git@gitee.com:lzt841/custom-script.git
lzt841
custom-script
自定义脚本解释器
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891