代码拉取完成,页面将自动刷新
使用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'
}
实现自定义执行命令,自定义关键词,以下示例实现#命令处理,实现打印功能
将方法定义关键词修改为中文“方法“,修改后对已加载的脚本无影响,大
部分关键词都可以修改,只要注意不建议修改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());
自定义全局方法,变量操作方法
测试用例代码脚本
//测试用例代码脚本
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());
添加变量基本类型,增加一个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());
实现自定义操作运算
#描述:
自定义操作运算符,增加一个复合运算符,实现 自定义‘$‘符,达到 10$2得到14的结果,就是左边加上两倍的右边的效果
示例
//测试用例代码脚本
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());
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。