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

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
🤔可悲不进步的 duangsuse 总是想弄一门自己的程序设计语言。
可不管他怎么写代码,连一个有函数和局部作用域的计算器都弄不出来。

于是他又准备设计一门……

这个计算器是Lambda calculus的计算器。
所谓的Lambda演算就是说,词法作用域(Lexical scoping),不过我还给它加上了 Haskell 一样的 Currying。
Lambda 就是 \formals. body abstraction
不过 currying 的 lambda 只能有一个参数,\formals 需要foldRight才行。
然后可以 apply: substitute + reduce
这次我不用解析组合子了(这是看项目而定,何况现在我已经写过很多,暂时没找到好的架构),手写基于 Feed 的递归下降解析器。

中缀链的解析算法选用普通的左折叠栈式。
一时间不打算支持自定义infix operator,因为我的trie树还没写对一个。
计算器是弱类型的。


下面的语法规则是这种格式:

名字 定义
——
也可能是

名字
定义
——
终结符(一个字符)的名字是小写
非终结符(一个子语法)的名字都是大写
但是 newline, whitespace 可以不大写

a b c 这是按顺序读这三项
为了表示上的方便,我们默认这三项里都可以有空格也即 a b c = a ws b ws c
  ws = {whitespace}?
然后 . 出现的地方不得自动加 ws

a | b | c
是读一项可能是a,b,c之一
{a} 是重复读a
(a)
是结合group的标记,无实际含义
a~t 是重复读取 a,直到 t 成立的意思 (不是 do while…… 只要t成立一遍都不会解析的)
a!t 是读取a,但 t 必须不成立才算的意思

anychar 是任何字符的意思
keyword 除了用来引用终结符,如果引用到的名字不存在则是表示读一"keyword"
然后,a? 表示a可能存在也可能没有

字符的表示基本和Kotlin一样,'a', \t(\的形式不必加'')
字符串也差不多
[] 和正则表达式差不多,不过支持 [ab(rule)] 这种格式
所谓的差不多就是 [123], [0-9a-z] 都支持这种

现在是语法规则
whitespace [ \t\n\r] | Comment
newline CRLF|lineFeed
CRLF "\r\n"
lineFeed '\n'

Comment "{-" (Comment|anychar) ~"-}"

File
{ Def|Expr.}
Def
def Name = Expr
|def Name Abstract
Expr
Let | Abstract | Apply
|If | InfixChain | Atom
Let
let Binding in Expr
Binding Bind { , Bind.}
Bind
Name = Expr

Abstract {— Reduce right —}
lambda LexicalScoped
LexicalScoped
Name LexicalScoped {— one substitution —}
|There Expr {— body with substitutions —}
There "->"
lambda bslash

Apply {— Reduce left —}
Expr Expr | Apply Expr

If
if Expr then Expr else Expr
InfixChain
infixChain(||; &&; ==,!=; <,>,<=,>=; +,-; *,/,%; **;)
Atom
Group | Unary | Name | Literal
Group '(' Expr ')'
Unary UnaryOps Atom
UnaryOps unary(!, -)
Name {— Single _ is reserved —}
(_ | [A-Za-z一-鿕]).{[_A-Za-z0-9一-鿕]}?
|`anychar~newline`
Literal
Integral | Rational | Boolean | Char

hexDigit [0-9a-fA-F]
NumpartP(digit)
digit | {digit _?} digit
Numpart NumpartP([0-9])
sign '+'|'-'

Integral
sign? . ("0b".NumpartP([0-1])
|"0x".NumpartP(hexDigit)
|[1-9].Numpart
|[0-9]).[lL]?
Rational
sign? . Numpart.(dot Numpart).([eE] . sign? . Numpart)?.[fF]?
Boolean
true | false
Char quote.(
anychar![(quote)(bslash)(newline)]
| EscapeSeq).quote
EscapeSeq
bslash.(UnicodeLiteral | Escape)
UnicodeLiteral
u . hexDigit.hexDigit.hexDigit.hexDigit
Escape
[tbnr(quote)(dqoute)(bslash)$]

dot '.'
quote '\''
dqoute '"'
bslash '\\'
我去看看 Kotlin 好像已经被绝大部分 Android 开发者采用了,真是良币驱逐劣币,可怜的 Jawa。

其实Kotlin细节上的实现难度还挺多的,比如 fun <E> E?when (val a = 1) 这种语法糖或者类型推导的
我这种描述式没入门是不可以的
就语言的实现而言,Kotlin显然不是入门选项
Forwarded from dnaugsuz
typealias PredicateOn<T> = T.() -> Boolean
typealias Producer<R> = R
fun <R> retry(n: Int, terminate: PredicateOn<T> = { true }, op: Producer<R?>): R? {
for (_i in 1..n) {
val res = op() ?: return null
if (terminate(res)) return res
}
return null
}

这么写岂不是也可以……
骚操作一点(不建议)甚至可以这么写
fun <R> retry(n: Int, terminate: PredicateOn<T> = { true }, op: Producer<R?>): R? {
for (_i in 1..n) {
val res = op() ?: return null
res.takeIf(terminate)?.let { return it }
}
return null
}

写成Java一样意义不明的风格是哪般……
真鼓吹数学的人都去学抽象代数机器证明了,次一点的也搞范畴论Monad学descriptive甚至逻辑式去了,应用编程就写好看点,为什么起名字喜欢起数学糟粕形式的名字……
难道还真应验了王某人的那句话『你越是看不懂越觉得自己智商低、越觉得我牛逼』……

也可以 functional inline
加个 inline 就行,然后如果报错就继续加 crossinline……
今天不开明天开。
duangsuse::Echo
我去看看 Kotlin 好像已经被绝大部分 Android 开发者采用了,真是良币驱逐劣币,可怜的 Jawa。 其实Kotlin细节上的实现难度还挺多的,比如 fun <E> E? 和 when (val a = 1) 这种语法糖或者类型推导的 我这种描述式没入门是不可以的 就语言的实现而言,Kotlin显然不是入门选项
表达这些语法的语言本身也很容易用它来描述:

whitespace [' '\t\n\r]
comment "{—" anychar|Comment ~"—}"
metachar
['[' ']' '(' ')' '{' '}'
'.' '|' '~' '!' '?'
(dquote) (bslash) (quote) '-']
Single
"anychar"
|Char
|SetChar
Char
anychar!metachar {— a, b, c —}
|EscapeSeq {— \t, \r —}
|quote.anychar.quote {— 'x' —}
SetChar {— [0-9A-Z(identchar)$#] —}
'['.{SetElement}.']'
SetElement
Char.'-'.Char
|'('.Name.')'
|Char

Complex OrComplex | Seq
{— a b c => a ws b ws c —}
{— a.b.c => a b c —}
{— ws {whitespace}? —}
Seq OrComplex { dot? OrComplex .}
OrComplex AtomComplex | Or {— a | b | c —}
Or AtomComplex { mid AtomComplex .}

AtomComplex
Grouped {— (a) —}
|NameRef_Keyword
|Keyword
|Repeat {— {a} —}
|Optional {— a? —}
|RepeatUntil {— a~t —}
|TakeUnless {— a!t —}

Grouped '(' Complex ')'
NameRef_Keyword Name
Name [_A-Za-z]. {[_A-Za-z0-9]}?
Keyword '"'. anychar!metachar|EscapeSeq ~'"'

EscapeSeq
bslash. (UnicodeLiteral | Escape)
UnicodeLiteral
u. hexDigit.hexDigit.hexDigit.hexDigit
Escape
[tbnr(quote)(dqoute)(bslash)$]

quote '\''
dquote '"'
bslash '\\'

AnyTerm Single | Complex
{— 终结符小写、非终结符大写、newline,whitespace作为非终结符可以小写 —}
Definition Name AnyTerm
File {Definition}
dnf.g
1.4 KB
#CS #PL ...想Swing和IDE
突然发现ANTLR也好方便啊
简直能阻止我继续写ParserKt了,不知道能不能handle上下文相关语法
发现按换行分项在 ANTLR 好难实现,考虑了一下我还改语法换用类似 GNU Make 的双空格算了
……ANTLR 单纯用常用的style就很难把JavaScript那种 ;NL 并取的策略加进去
DNF.g4
1.5 KB
发一下我内联所有Token前的……
功能尚不正确
DNF.g4
1.3 KB
duangsuse::Echo
DNF.g4
ANTLR 是很好用,可惜我还是打算先弄出 ParserKt 再拿它解决 PKNF(ParserKt Normal Form) 的问题,我已经拿 PKNF 定义了 PKNF 了。
浪费了一天时间写了一篇纯函数式编程半通不通半理论不实践的教程…… 咱来谈谈范畴论
最后一张预览,要的自己看,我可懒得再看一遍了…… 还有 binarie 和 ParserKt、PKNF 要想办法而且还有东西要发啊……
才发现,烂代码对生活的危害就那么严重。
最后还不得不学了点C++…… 虽然大部分我都是自己回忆的语法