🤔可悲不进步的 duangsuse 总是想弄一门自己的程序设计语言。
可不管他怎么写代码,连一个有函数和局部作用域的计算器都弄不出来。
于是他又准备设计一门……
这个计算器是Lambda calculus的计算器。
所谓的Lambda演算就是说,词法作用域(Lexical scoping),不过我还给它加上了 Haskell 一样的 Currying。
Lambda 就是
不过 currying 的 lambda 只能有一个参数,
然后可以 apply: substitute + reduce
这次我不用解析组合子了(这是看项目而定,何况现在我已经写过很多,暂时没找到好的架构),手写基于 Feed 的递归下降解析器。
中缀链的解析算法选用普通的左折叠栈式。
一时间不打算支持自定义infix operator,因为我的trie树还没写对一个。
计算器是弱类型的。
下面的语法规则是这种格式:
—
名字 定义
——
也可能是
—
名字
定义
——
终结符(一个字符)的名字是小写
非终结符(一个子语法)的名字都是大写
但是
为了表示上的方便,我们默认这三项里都可以有空格也即
字符的表示基本和Kotlin一样,
字符串也差不多
所谓的差不多就是
现在是语法规则
可不管他怎么写代码,连一个有函数和局部作用域的计算器都弄不出来。
于是他又准备设计一门……
这个计算器是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细节上的实现难度还挺多的,比如
我这种描述式没入门是不可以的
就语言的实现而言,Kotlin显然不是入门选项
其实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……
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…
Telegram
duangsues.is_a? SaltedFish
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…
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…
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}发现按换行分项在 ANTLR 好难实现,考虑了一下我还改语法换用类似 GNU Make 的双空格算了
……ANTLR 单纯用常用的style就很难把JavaScript那种 ;NL 并取的策略加进去
……ANTLR 单纯用常用的style就很难把JavaScript那种 ;NL 并取的策略加进去
duangsuse::Echo
DNF.g4
ANTLR 是很好用,可惜我还是打算先弄出 ParserKt 再拿它解决 PKNF(ParserKt Normal Form) 的问题,我已经拿 PKNF 定义了 PKNF 了。
浪费了一天时间写了一篇纯函数式编程半通不通半理论不实践的教程…… 咱来谈谈范畴论