duangsuse::Echo
717 subscribers
4.26K photos
130 videos
583 files
6.48K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
duangsuse::Echo
考虑一下我们的『简单形式』代码: (httpMethod '@')? interfaceName '(' Args ')' ('->' ReturnType)? '=' UrlPath 而 Args 形似 itself repeatItSelfWithPrefix(','): name '?'? OptionVals? 而 OptionVals 形似 '{' val (',' val)* '}' 而 ReturnType 形似 (arrray|object) ':' AtomicObject…
总结一个 PEG 文法:

File := Interface*

Interface :=
(httpMethod '@')? interfaceName '(' Arg* ')' ('->' ReturnType)?
'=' urlPath

Arg := argName '?'? OptionVals? (',' Args?)

OptionVals := '{' possibleValue (',' possibleValue)* '}'

ReturnType
:= (array|object) ':' Atom
| '[' propertyName (',' propertyName) ']'
| Atom

Atom := (ReturnType|number|string|entityName)


... 等等,我们还没有支持这些语法(其实目标是拿来生成 Boilerplate code 的,不过要这些也有用)

index() -> object<category,object<operation,linkTemplate>>

version() -> plain

====

upTime() -> (string|number):datetime
detailedInfo() -> object:<prop,desc>

上面两个还是不要支持,要支持在 Kotlin 项目上可能也没有多大价值

PUT@transferAppOwner(aid,uid) -> object:[aid,old,new]
DELETE@deleteAppUpdate(aid,rev) -> object:appUpdate

这两个是自己有问题,也不支持

listMetaUser(sort?{created,followers},sliceFrom?,sliceTo?) -> array:object:user

这个也是

好了,那么修改完上面两个,就能拯救 GeekApk 目前 API 文档的烂摊子... 😕
GeekSpec := ApiSpec*

ApiSpec :=
(httpMethod '@')? interfaceName '(' Arg* ')'
('->' (ReturnType|Dict))? '=' urlPath

Arg := argName '?'? OptionVals? (',' Arg?)

OptionVals := '{' possibleValue (',' possibleValue)* '}'

ReturnType
:= (array|object) ':' Atom | Atom

Atom := (boolean|number|string|datetime|plain|entityName|)

Dict := '[' DictItem (',' DictItem)* ']'
DictItem
:= ReturnType
| '$' fieldName ':' ReturnType

https://pegjs.org/online

的编辑器没有自动保存,我写了半天的内容全丢了。
真棒。


一方面有我自己没有理清递归思路,还有莫名其妙使用递归解析规则的原因,我开始根本没想到自己应该在写逻辑的时候好好想想这是递归的解析规则

另一方面,PEG.js 自己就有问题,我绑定给它的参数它有时候都弄不正确,? 语义根本行为莫名其妙,真是令人琢磨不透,害得我查了半天还找不到解决方案,最后自己勉强弄了一个能用的 workaround
duangsuse::Echo
GeekSpec := ApiSpec* ApiSpec := (httpMethod '@')? interfaceName '(' Arg* ')' ('->' (ReturnType|Dict))? '=' urlPath Arg := argName '?'? OptionVals? (',' Arg?) OptionVals := '{' possibleValue (',' possibleValue)* '}' ReturnType := (array|object) ':'…
递归的规则能不要写就不要写

上面那个

Args := argName '?'? OptionVals? (',' Args)?
就应该写成 Repeat 组合子的形式,一上来就递归很难受,免得麻烦,要知道 PEG.js 那个鸡肋破真参数绑定工作下,即使好好分析了也未必能做到解决问题... 难受的很
{
let filterIsNotArray = (xs => xs.filter(x => !Array.isArray(x)));
let isNotUndef = (s => s != 'undefined');
let head = xs => xs[0]
}

GeekSpec = specs:(_ ApiSpec _)* { return specs.map(x => head(filterIsNotArray(x))); }

entityName = letter+
interfaceName = letter+
urlPath "url letters" = (letter / [:/.{}?&=%])+
argName = letter+
possibleValue = letter+
fieldName = letter+

httpMethod
= "GET" / "POST"
/ "PUT" / "DELETE"
/ "PATCH" / "OPTIONS"
/ "HEAD"

ApiSpec =
name:interfaceName _ '(' _ a:Arg? as:(_ "," _ Arg)* _ ')'
_ rtxd:('->' _ (ReturnType / Dict))? _ '=' _ url:urlPath {
return {
method: "GET",
name: name.join(''),
args: a == null ? [] : [a].concat(as.map(ra => ra[3])),
return : isNotUndef(typeof rtxd) ? (rtxd == null ? null : rtxd[2]) : null,
url: url.join('')
};
}
/
method:httpMethod '@' name:interfaceName _ '(' _ a:Arg? as:(_ "," _ Arg)* _ ')'
_ rtxd:('->' _ (ReturnType / Dict))? _ '=' _ url:urlPath {
return {
method: method,
name: name.join(''),
args: a == null ? [] : [a].concat(as.map(ra => ra[3])),
return : isNotUndef(typeof rtxd) ? rtxd[2] : null,
url: url.join('')
};
}

Arg = name:argName _ q:('?' / "!"?) _ ov:OptionVals? {
return {
name: name.join(''),
required: q != "?",
options: ov
};
}

OptionVals = '{' _ pv:possibleValue? pvs:(_ ',' _ possibleValue)* _ '}' {
return pv == null ? [] : [pv.join('')].concat(pvs.map(rpv => rpv[3].join('')));
}

ReturnType
= axo:("array" / "object") ':' a:Atom { return { type: axo, of: ((typeof a != 'string') ? a.join('') : a) }; }

Atom = "boolean" / "number"
/ "string" / "datetime"
/ "plain" / entityName

Dict = '[' _ di:DictItem? dis:(_ ',' _ DictItem)* _ ']' {
return di == null ? [] : [di].concat(dis.map(rd => rd[3]))
}

DictItem
= ReturnType
/ '$' name:fieldName ':' rt:ReturnType {
return { name: name.join(''), type: rt };
}

_ "whitespace"
= [ \t\n\r]*

letter "letter"
= [A-Za-z0-9_\-]


大功告成(其实一点都不,而且某非常智障的 duangsuse 没有用到递归(因为这个玩意开始的时候我输入的数据比较多,分析起来当场爆炸留下心理阴影,列表处理方法我又到处乱用 e.g. splice, reverse, concat 却没有想到 map...),非常不爽) #Parser #PL

管他呢反之先火速处理(
geekspec_parser.js
56.4 KB
#PL #DSL GeekSpec DSL 的解析器,准备写代码生成来的
duangsuse::Echo
{ let filterIsNotArray = (xs => xs.filter(x => !Array.isArray(x))); let isNotUndef = (s => s != 'undefined'); let head = xs => xs[0] } GeekSpec = specs:(_ ApiSpec _)* { return specs.map(x => head(filterIsNotArray(x))); } entityName = letter+ interfaceName…
简单点来说,GeekSpec DSL 使用类似如下语法:

GET@methodName(args) = urlTemplate
如果 使用的 HTTP 动词是 GET,那 GET@ 可以省掉不写

args:
argName! argName?
如果不写 ? 或者 !,默认 ?(可选参数)
后面还可以跟 {} 包含参数值选项

返回值:符合 JSON 规范,开头必须是 object: 或者 array:,不支持递归类型(比如 array:array:string)

后面可以跟 boolean/number/string/datetime/plain/entityName(随便什么其他的东西)

返回值也可以是 Dict:
[]

dict 组织一打不同的返回值,其实是 object(不关心属性名),可以为他们命名

[$name:type]
duangsuse::Echo
geekspec_parser.js
GeekApk 的 v1b API 规范正在使用 GeekSpec DSL 语言重写
好像能用
duangsuse::Echo
好像能用
目前勉强能用的 GeekSpec DSL 将能够解决不断手工重复代码的问题,起码可以大大提升以后的接口开发效率

当然你也可以喷我不上 RESTful 所以才有这些鬼事情,不过我觉得没什么。
duangsuse::Echo
GeekApk 的 v1b API 规范正在使用 GeekSpec DSL 语言重写
https://github.com/duangsuse/GeekApk/blob/add-controller-template/code_writer.js

现在已经开始使用 Code Writer JavaScript 脚本自动生成测试了,明天会尝试使用它生成 Timelinecontroller

GET user/{id}/checkHash: checkHash(id-path:UserId!,hash:String!)
Returning object of string

GET user/all: listUser(sort:String?,sliceFrom:UserSize?,sliceTo:UserSize?)
Returning array of GeekUser


所作额外的扩展是一些内部类型信息上的,这样就可以用它来生成 Kotlin 控制器接口,只需要简单调用一下 String.prototype.split 就可以。
所有参数名都可以额外有 :TypeName 类型信息,optional 的参数(?参数)自动适配 Kotlin 可空类型系统

参数名也可以额外有 - 位置信息,目前可选项为 body 或 path,对应 Spring Controller Binding 的 @RequestBody@PathVariable

目前的生成效果(未来将被替换为生成 Kotlin 代码)
还是会用 radare2 的 ( #reveng
Forwarded from 羽毛的小白板
可以得出一个链表每项后代的方法。这里也用到数组作为中间状态避免链表里的环导致无法结束迭代 #Pg好玩
目前,GeekSpec 的 Kotlin/Spring 代码生成器已经可以勉强正常工作,不过不是所有特性都测试到了,目前借助强大的 IDEAc 它的效果很好,一 瞬 生 成了之前我要写到手软的代码,大家可以来体验一下(虽然一点都不 production ready,有点像那种小脚本
duangsuse::Echo
目前,GeekSpec 的 Kotlin/Spring 代码生成器已经可以勉强正常工作,不过不是所有特性都测试到了,目前借助强大的 IDEAc 它的效果很好,一 瞬 生 成了之前我要写到手软的代码,大家可以来体验一下(虽然一点都不 production ready,有点像那种小脚本
目前的版本它的作用就是把这种 GeekSpec 代码:

searchUser(type:String?{username,nickname,bio}, kw-path:String, sort:String?{created,followers}) -> array:GeekUser
= user/search/{kw}

翻译成 Kotlin + Spring Controller bind 的控制器接口

@GetMapping("user/search/{kw}")
@ResponseBody
fun searchUser(@RequestParam("type") type: String/* Maybe username or nickname or bio */, @PathVariable("kw") kw: String, @RequestParam("sort") sort: String/* Maybe created or followers */): List<GeekUser> {
TODO()
}

目前仍然有不少小瑕疵正在修改
This media is not supported in your browser
VIEW IN TELEGRAM
(因为我的 Throws 上面已经有了,这里转发个大佬的(

当作自己的祝福喽(因为人类的本质是...
Forwarded from 羽毛的小白板
祝大家新年快乐