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
开源在 GitHub:Essay-DIP-BMP-SIMD-Operation

正在准备完成剩下的东西...

这周事情挺多的,看来又是做不完 👉

Essay-Fibonacci-Generator
Essay-Cached-Factorial-Stream
Essay-Java-Annotations
(Runtime, Processor, Andriod)
JavaPrefer (Preference DSL)
Essay-Android-Lime-Tokenize
(Fragments & Item model)
Java BinaryStreamIO
Essay-Compilers-LLVM-Toy
Essay-PL-Java-Miniλ
GC For the Radium Programming Language
duangsuse::Echo
开源在 GitHub:Essay-DIP-BMP-SIMD-Operation 正在准备完成剩下的东西... 这周事情挺多的,看来又是做不完 👉 Essay-Fibonacci-Generator Essay-Cached-Factorial-Stream Essay-Java-Annotations (Runtime, Processor, Andriod) JavaPrefer (Preference DSL) Essay-Android-Lime-Tokenize (Fragments & Item…
#task 这周本来还计划继续讲 Type system 相关的话题和:

Y 组合子,会从其求值时(beta reduction)的着重点 — (\x -> f (x x)) 是做什么的、为啥它能使 f 被执行、n+1 层函数的返回值如何传到 n 层函数去、递归停止的时候是怎么停下来的

找到了一些书上可能的错误点,也会讲一些

Lambda 的 Bool 和 Natural Number Abstraction、Monoid (含宏半群)

Bluetooth Socket (Java InputStream) IO 的使用,当然是写代码的

8085 嵌入式 MCU (微控制器)上的 DJNZ & NOP 延时(sleep)

Java Swing 基本图形组件的低效 LCD 滚动实现,当然是代码

关于位运算“加速”的一些科普常识(扫盲,因为不研究原理、而且实在不是很困难的内容)和使用注意(推荐还是不要使用)

进制是什么、大小端存储(这次是修复,因为我看完《嵌入式》,当然不是真正的书名后发现我的知识比那几位博士是多么浅薄,我居然连进位计数法都不科普出来,说明我不是真正理解,而且我对大小端的描述还不够具有实用性,不好理解,可能有很大理解偏差)

POSIX 的 (异步模式) Signal 是否会嵌套问题(看《嵌入式》到 MCU/MPU 的异常系统、异常处理、优先级、屏蔽、嵌套想到的,做实验)

LLVM Cookbook 的一些评论性质内容,比方说,吐槽它的代码风格不一致、排序不适合学习者记忆

关于学习方法的一些东西
duangsuse::Echo
开源在 GitHub:Essay-DIP-BMP-SIMD-Operation 正在准备完成剩下的东西... 这周事情挺多的,看来又是做不完 👉 Essay-Fibonacci-Generator Essay-Cached-Factorial-Stream Essay-Java-Annotations (Runtime, Processor, Andriod) JavaPrefer (Preference DSL) Essay-Android-Lime-Tokenize (Fragments & Item…
#Algorithm duangsuse 对 Vec 之类列表数据结构上操作的简记法:

SSRSIDCC —

Search, Sort, Rotate (reverse), Slice (subsequence), Insert (all), Delete (many), Copy, Concat

当然,还有 length, get, set, equality 什么的基本是必要的操作没有说、push, pop, (cons, head, tail) 之类特定 List 的也没有说,泛型和型变 subtyping 的不用找了(据说最近设计的 Ra 静态类型语言就支持了参数化类型,而且支持 Kind (type constructor)、Type Operators 和 Traits),创建遍历器也是必须的

对 Set,集合来讲,还有

Contains (all), Retain (取交集)
duangsuse::Echo
#Algorithm duangsuse 对 Vec 之类列表数据结构上操作的简记法: SSRSIDCC — Search, Sort, Rotate (reverse), Slice (subsequence), Insert (all), Delete (many), Copy, Concat 当然,还有 length, get, set, equality 什么的基本是必要的操作没有说、push, pop, (cons, head, tail) 之类特定 List 的也没有说,泛型和型变 subtyping…
这个主要是为了预备 Ra 解释器实现(当然 Ra 计划的实现是基于编译器 / 虚拟机结构的,受到 Lua 的启发,或者说基本是抄 Lua 的,我只是代码的复写工)ADT 系统的

Product Type(Structure)、Sum type(C 里的 Union + Enum 合体)实现被称为 dat,又是另一回事了

面向对象的多态特性(因为 Ra 还是支持 subtyping 和部分面向对象的,而且它还支持 Trait 和自动查找 Trait instance,虽然 Ra 编译器的目标语言是动态处理类型的...)

Ra 支持一个 abstract fun:

type Class = Nat

data Person { name :: Str }

data Student is Person
{ grade :: Nat * class :: Class }

data Teacher is Person
{ classes :: Vec of Class }

abstract fun Person.briefDesc(): Str = "a person called $name"

let p = Person { name: "duangsuse" }
println(p.briefDesc!) — "a person called duangsuse"

— 架构器怎么调用?只要是在一个文件里的单位,编译器知道 Person 结构的布局,这个文件里的所有函数都可以随便架构 Person
— 否则的话,(即便通过 extern 编译器可以知道 Person 的结构)不允许直接构造 Person 实例,这是语言访问控制特性

— 使用多态,Rewrite 是 Ra 里进行尾递归的关键字,可是 Ra 不要求尾递归函数写上可能作为 modifier 的 rewrite(因为这类函数一般主体不长,很容易看到定义里的 rewrite,说起来 Ra 不支持基于控制流分析的自动 tailrec,如果你连自己是在尾递归都不知道就写尾递归算法,我觉得很有问题,另一方面,我现在不会做这种分析,就像我也没有办法去做寄存器(虚拟机的栈帧局部变量)高效分配需要的活动周期和数据依赖分析一样)
— Person. 是可选的,反正 Ra 不像 Java 是『面向对象语言』,如果 Person 上面还有其他版本的 briefDesc,直接用 (this :: SomeType).briefDesc! 就可以了

rewrite Person.briefDesc for Student = "a student in class $class called $name"
rewrite briefDesc for Teacher where
return "teacher $name for classes $[classes.join ', ']"

然后你可以使用 Ra 的多态特性,它是这么实现的:虚表、Rust 里的 trait objects。不过很多时候 Ra 的 Trait instance 只需要静态检查动态获取,因为它后面还是计划有一个虚拟机做模块化支持的(使用 Mangel 就可以了)。
这里没有提到自定义架构器,以后可能会说,已经设计好了带语法糖的东西

let me: Person

me = Student { class: 1, name: "duangsuse" }
me.briefDesc! -- a student in class 1 called duangsuse
me = Teacher { classes: Vec(of Nat).of(1, 8) } is Person { name: "Mr. Dalao" }
me.briefDesc! — teacher Mr. Dalao for classes 1, 8

讲个笑话,我可以玩玩《你的名字》的梗... 打住。
Vec(of Nat) 是可以自动推导的
Ca {...} is Cb {...} (is Cc {...})... 这种语法是可以解析的,因为虽然有点不对称(is 自己在这种情况下也有效)
但是没有大问题,无非就是提供个备选项 (Ca {...} is Cb {...}) :: Bool(而且多态类型系统上可以描述 infixl Any (is) Data 的类型,问题是我现在不会写类型推导算法),默认是架构子类型

Ra 的多态也是使用类似 C++ 虚表的方法实现的,不过 Ra 支持的多态不多,也不支持多继承,但可以 mixin traits

当你定义拥有 abstract 操作符 (self fun) 的类型时,Ra 编译器自动为这个类型(必须 data,也即 Product type,因为 Sum type 可以直接进行判断没有必要使用多态)

data Person { name :: Str * _briefDesc :: <Self> -> Str }

这里 Self 在 Ra 里有 self 的作用域里指代当前定义的类型 — Person

T -> R
是 Ra 里的类型操作符,它接受两个类型返回一个类型 (Kind 是) (* -> * -> *)

type infixr T@(*) -> R is Fun of (T, R)

所以 Self:fmt :: Self -> const Str -> Ary of Any -> Self 是说 Fun of (Self, Fun(of Str, Fun(of Ary of Any, Str)))

不过说起来,Ra 的确不打算支持依赖类型... (fmt 的类型可以依赖于编译期根据它的参数计算,这样就可以实现类型安全的 printf,对 Ra 来说还是有类型检查期这个概念的,不像很多动态类型检查的语言)

Kotlin 被作为内建支持的 Optional 也是作为 Type operator 实现的
unary type postfix T@(*)? is Optional of T

<Self> 是一个元组,它实现上类似 data,可是其成员没有名字

异常系统和也是支持的、在 Ra 里,Continuation 也是 first-class、Coroutine 通过 continuation 实现,Ra 的协程和 Lua 不一样,它不是一个类似线程的 State,而是一个(不稳定,但是可以用)的执行状态,包括所有 Lexscope 引用,可以随便传递(基本被包装成 Iter 方法的形式,Instric 函数),也可以实现基于 Continuation 的异常
明天我会把之前我写过 #Kotlin 的 Ascii 字符画生成重写一下。

使用到非常简单的 API:

java.awt.Color
java.awt.BufferedImage

javax.imageio.ImageIO

javax.imageio.ImageIO.open
javax.imageio.ImageIO#getRGB
面向对象需要定义一个类的事情,使用函数式可以简单地用 Currying 完成呢(比如说,java.lang.Comparable 其实不过就是 T.(Any) -> Boolean 嘛,只是给这种 self 函数起了个高大上的名字)
彻底给跪,我重写一个普适性不那么强的吧...
算了,还是去写点别的...
duangsuse::Echo
彻底给跪,我重写一个普适性不那么强的吧...
还不如我给 java.awt.image.BufferedImagetypealias Point2D = Pair<Axis2D, Axis2D> 写的 operator extension 好用呢...
说起来,Java 也不给内置一个动态 Number 自动转换提升功能... 非得都成 BigInteger 或者都成 Long 再转换好低效的说...
Kotlin 抽象的威力、扩展方法、operator overloading,typealias、闭包
duangsuse::Echo
开源在 GitHub:Essay-DIP-BMP-SIMD-Operation 正在准备完成剩下的东西... 这周事情挺多的,看来又是做不完 👉 Essay-Fibonacci-Generator Essay-Cached-Factorial-Stream Essay-Java-Annotations (Runtime, Processor, Andriod) JavaPrefer (Preference DSL) Essay-Android-Lime-Tokenize (Fragments & Item…
看起来全 TM 没时间写了,不过不要紧,因为过两天高考放假(这里希望各位都能取得好成绩哦~我知道很多同学也都是很努力的,努力就一定有收获呢)
我还会回来的~ 🐱

所以,我要先巩固一下 LLVM Cookbook 的学习结果

写一下 Toy 语言的前端和代码生成器、JIT、FunctionPass

这些是编译器工程师的基本操守,大概从会写 (for LLVM like frameworks) Pass 开始就算是一个 CE 了,加油哦。
duangsuse::Echo
看起来全 TM 没时间写了,不过不要紧,因为过两天高考放假(这里希望各位都能取得好成绩哦~我知道很多同学也都是很努力的,努力就一定有收获呢) 我还会回来的~ 🐱 所以,我要先巩固一下 LLVM Cookbook 的学习结果 写一下 Toy 语言的前端和代码生成器、JIT、FunctionPass 这些是编译器工程师的基本操守,大概从会写 (for LLVM like frameworks) Pass 开始就算是一个 CE 了,加油哦。
这个编译器接受这样的语法,它的 tokenizer 基本是使用 libc 的 API(e.g. isalpha, isalnum, isspace from ctype.h)弄的

然后数值的解析基本都是 atoi, strtod 什么的。

这里,因为我实在是没有时间,所以支持特性的很少

以后我会给它增添浮点类型(就是所谓的 “两种基本类型:Long integer 和 Double Prec floating point,不支持类型组合”)first-class 支持
和支持 hex, binary 字面数值


— 语言部分

这是一门简单的计算器语言:它支持 Int 值,可以进行 +, -, *, / 操作
可以进行 function definition:def <name> ArgList Expr


Tokens

数值:C isdigit+
标识符(名字): C isalpha isalnum*
空格:C isspace+
注释:支持行注释,以 # 开始直到 "\n", 换句话说,自动支持 CRLFLF, 可怜的 Windows!(默认 CR \r
好吧,因为原版支持了 \r,我也支持一下算了,于是 Windows 也可以用

data Token
= Num Int | Iden String
| Spaces Count | Comment String
| KwDef | ExpGrouper Bool
| Operator | EOF

EOF:因为不用 EOFException,什么?Haskell 为什么不用 Maybe 或者 Exception Monad?因为这是要用 C 写的!

关键字在词法上和标识符采取一样的规则:
def

只要不是上面任何一种,即为 Operator,支持:

() 作为表达式组合的标记
, 作为列表分割的记号
infixl + < - < * < /

就有这样的语法 EBNF 规则,首先我们想的是 def 定义函数,entry 规则是一个表达式组,我们可以像 REPL 一样用:

Entry -> Expr

TERMINATOR def, extern
TERMINATOR SYMBOL +, -, *, /, COMMA, (, )

Expr -> Primary . "BinRhs" (binop Primary)*
where binop -> '+' | '-' | '*' | '/'

Params -> Expr (',' Expr)* -- Recursive
Names -> ident (',' ident)*

Primary ->
"Derefing" (IdentRef | IdentCall) | NumLit | ParenExpr
where
ParenExpr -> '(' Expr ')' -- Recursive
NumLit -> number
IdentRef -> ident
IdentCall -> ident '(' Params? ')'

— Function
FunTypedef -> ident '(' Names? ')'

ExternFun -> extern FunTypedef
DefFun -> def FunTypedef Expr

这样就可以弄出这些示例:

def add2(x) x +2

DefFun {
type { name "add2"; args (!!) ["x"] }
body {
Expr {
me IdentRef(ident("x")); op '+';
rhs NulLit(number(2))
}
}
} (1)

def sub1mul3(x) (x-1)*3
def add(x, y) x + y
def sumWeight(x, wei) (x-1) + wei*10

然后我们又是分开来 handle FunDef 和 Expression 的求值的
REPL 其实就是

forever do {
expr = parse!(gets)
expr.evaluate!
}

其中 Expr.evaluate!

when expr {
is Expr -> puts this.treeEvaluate!
is ExternFun -> module.externFunction(this.type)
is DefFun -> module.createFunction(this.type, this.body.compileAll())
otherwise -> error("Cannot handle expression $this.name")
}

比如上面的例子 (1),只需后序遍历 AST 就可以面向某种目标生成代码:

walk DefFun {
val signature = type.createSignature()
unless my_module.hasExtern(signature) and my_module.hasDefinition(signature)
my_module.createFunction(signature, body.walk())
else error("Defined function does not match (for $signature)")
}
walk Expr: Value {
return when op {
'+' then Builder.createI32Add(me.walk(), rhs.walk(), "sum")
otherwise error("Unknown operator $op")
}
}
walk IdentRef: Value {
return scope.get(name).mapError { error("Failed to resolve symbol in context $scope.ctx") }
}

walk NumLit: Value {
ConstantInt.get(Types::getInt32Ty(my_ctx), value)
}

LLVM 是基于 SSA 架构的,这个架构假设 LLVM 有无数个机器寄存器,它的指令结构是线性的,但是任何指令的结果都可以被其他指令引用而无须显示指定目标存储器,这样的指令虽然是线性序列,但实际上可以被归纳到树形结构或者从树形结构被创建

比如 data Ast = Lit Int | Add Ast Ast(Haskell)

eval :: Ast -> Int
eval (Lit x) = x
eval (Add lhs rhs) = eval lhs + eval rhs

evalSsa :: Ast -> Value
eval (Lit x) = constInt x
eval (Add lhs rhs) = add (eval lhs) (eval rhs)

上面的 walker 对于 (1) 应该能生成这种代码(LLVM IR)

define i32 @add2 (i32 %x) {
entry:
%sum = add i32 %x, 2
ret i32 %sum
}

这是对 Ast 进行后序遍历(一种括扑排序)的结果,当然有多少个 Add 都一样,结果肯定是正确的

def add2_3(x) 3+(x+2)

define i32 @add2 (i32 %x) {
entry:
%sum = add i32, %x, 2
%sum1 = add i32 %sum, 3
ret i32 %sum1
}
duangsuse::Echo
这个编译器接受这样的语法,它的 tokenizer 基本是使用 libc 的 API(e.g. isalpha, isalnum, isspace from ctype.h)弄的 然后数值的解析基本都是 atoi, strtod 什么的。 这里,因为我实在是没有时间,所以支持特性的很少 以后我会给它增添浮点类型(就是所谓的 “两种基本类型:Long integer 和 Double Prec floating point,不支持类型组合”)first-class 支持 和支持 hex, binary…
试用! #CE

source_filename = "add.ll"
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"duangsuse version ??? (Fedora 7.0.1-6.fc29)"}

define i32 @add2_3 (i32 %x) {
entry:
%sum = add i32 %x, 2
%sum1 = add i32 %sum, 3
ret i32 %sum1
}

declare i32 @printf(i8*, ...)

@.fmt = private unnamed_addr constant [9 x i8] c"res: %i\0a\00", align 1
define i32 @main (i32, i8**) {
enrty:
%res = call i32 (i32) @add2_3(i32 3)
%_r = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.fmt, i32$
ret i32 0
}


[DuangSUSE@duangsuse]~/projects% lli add.ll
res: 8
#Low #China 🌚
Forwarded from XiNGRZ's (XiNGRZ)
响应国家号召,本频道明天将进行技术升级,停更一天。
#PL #PLT 推荐,当然本频道之前也有讲类似的东西,非常感谢 LEXUGE 一直能关注这种弱鸡频道...(是的,在同为非应用向编程的人前我丝毫不会感觉自己容易被喷、或者对方要秀我一脸什么的,而且也不会莫名“自大” — 这类人显然在我说些可能他们还没学到的东西时候不会有任何不适感,各弄各的) 因为我在 GitHub 更新上看到他 Star 我最近改名的一个 repo,说起来,最近大家(一些可以认为是『学院派』一点的博主)都开始用纯 English 写作了,有点看不懂的样子,看来英文还是得学啊