当前位置: 首页 > article >正文

简单程序语言理论与编译技术·22 实现一个从AST到RISCV的编译器

本文是记录专业课“程序语言理论与编译技术”的部分笔记。

LECTURE 22(实现一个从AST到RISCV的编译器)

一、问题分析

1、完整的编译器(如LLVM)需先完成AST到IR的转换,并进行代码优化,再到汇编,如下图:

本次实验实现从AST到RISC-V(不考虑优化)的翻译。对于汇编,我们可以访问Compiler Explorer (godbolt.org)来熟悉一下,或者参看类似下图的手册(包括汇编指令和寄存器类型等内容):

对于本次实验,我们用 x8 作为帧指针(fp) 来固定指向当前函数的栈桢基址,使用 a0 寄存器作为表达式计算结果的默认存放寄存器(函数返回值也约定放在 a0)。为了方便,我们避免寄存器分配,转而使用栈来保存局部变量和临时数据。如下图:

2、首先考虑Binop的翻译,我们参考下图:

对于Int 常数类型,我们直接将常数值加载到寄存器,对应RISC-V 提供的 li(load immediate)伪指令,将一个立即数载入寄存器。而对于Binop,我们考虑处理三种情况:
• Num Binop Num E.g. 2 + 7
• Num Binop Num Binop Num E.g. 2 + 6 * 9
• (Num Binop Num) Binop (Num Binop Num) E.g. (2 + 6) * (8 + 7)

基本流程:计算左操作数 -> 保存左值 -> 计算右操作数 -> 运算合成结果。如此之后,我们可以使用两种方案来保存:
• 将左值保存在临时寄存器中(例如t0 ),再计算右值到另一个寄存器(例如t1 )
• 采用栈,在计算左值后将其压栈保存,计算右值后再弹栈取出左值进行运算

3、然后考虑Let的翻译:

这里我们需要增加对变量和作用域的支持,使编译器可以处理let绑定和变量引用。我们需要考虑栈帧与变量地址,当进入一个新的let块,需在栈上分配新的空间保存局部变量的值(调整栈指针fp的offset)。然后在编译阶段也需要一个映射(符号表)跟踪当前作用域中每个变量名的栈偏移或寄存器位置(env)。

据此,我们简化逻辑,使fp固定不动,变量x偏移可以按照进入let的次序决定。而固定帧具体而言,使在程序一开始一次性分配最大所需栈,然后fp = sp固定。

我们可以考虑一个流程:先编译e1得到其值在a0,然后在栈上分配空间保存其值 (addi sp, sp, -8)。接着将变量名x映射到新空间的位置,例如offset += 8表示又用8字节,映射env新增 (“x”, of fset)。随后编译内部表达式e2,Var x按env找到偏移,完成后回退栈指针释放x所占的空间 (addi sp, sp, 8 )。最后恢复符号表,弹出env中x的绑定。

4、然后考虑If的翻译,我们参考下图:

我们需要扩展编译器支持Bool常量和If条件表达式,正确处理程序的控制流,为每个if表达式生成唯一的标签并正确安插跳转指令,且保证程序无论哪一种路径都会结束。

具体的,思考同前面let/变量的交互:使用标签而非固定地址。条件的各分支内部可以定义自己的变量且作用域仅限于分支内,实现需以递归的方式编译分支表达式并各自管理环境。在分支中寄存器值通过a0返回结果,无残留的临时变量,无需专门清理分支的栈。

5、最后考虑Func和App的翻译。扩展编译器支持函数定义(Func)和函数调用(App),函数的闭包包含函数代码和绑定环境的组合体。而编译Func(x, body)会产生两部分输出:全局函数代码(储存待后续统一输出),以及当前表达式计算时创建闭包的代码。

回顾Riscv寄存器,caller负责传递参数(a0-a7寄存器)和保存临时寄存器;callee可以自由使用临时寄存器但需保存和恢复caller保存寄存器(如s0等)。我们在实现时仅考虑函数调用一个显式参数,并需要注意传递参数值和环境指针。

闭包的环境结构(仅提供一种思路):分配一块内存,大小足够存放一个指针加上所有自由变量的值;将函数的代码地址写入这块内存的开头;将该函数的自由变量当前环境中的值写入内存的后续字段;将指向这块内存的指针作为闭包值放入某寄存器。

注意,Func需要一个唯一的新函数标签(类似if的标签)。对函数体进行的编译,与main形式上相同(env -> local_env + closure_env)。在main函数体换Func编译结果为上一步的闭包分配代码。

而App调用有多种方式:jalr、call等。以jalr为例:将a0(闭包指针)保存到t0,a0放参数;使用ld t1 , 0(t 0)加载闭包偏移为0处内容(代码地址)到t1;jalr ra, t 1进行跳转调用。

其余包括free、优化等问题不在本节内容之内。

二、前置代码

1、lib/ast.ml

type binop = | Add| Sub| Mul| Div| Leqtype expr =| Int of int| Var of string| Bool of bool| Binop of binop * expr * expr| Let of string * expr * expr| If of expr * expr * expr| Func of string * expr| App of expr * expr

2、lib/lexer.mll

{open Parser
}rule read = parse | [' ' '\t' '\n'] { read lexbuf }| '+' { PLUS }| '-' { MINUS }| '*' { TIMES }| '/' { DIV }| '(' { LPAREN }| ')' { RPAREN }| "<=" { LEQ }| "true" { TRUE }| "false" { FALSE }| "let" { LET }| "=" { EQUALS }| "in" { IN }| "if" { IF }| "then" { THEN }| "else" { ELSE }| "->" { ARROW }| "fun" { FUNC }| ['0'-'9']+ as num { INT (int_of_string num) }| ['a'-'z' 'A'-'Z']+ as id { ID id }| eof { EOF }| _ { failwith "Invalid character" }

3、lib/parser.mly

%{open Ast(** [make_apply e [e1; e2; ...]] makes the application  [e e1 e2 ...]).  Requires: the list argument is non-empty. *)
let rec make_apply e = function| [] -> failwith "precondition violated"| [e'] -> App (e, e')| h :: ((_ :: _) as t) -> make_apply (App (e, h)) t
%}%token <int> INT
%token <string> ID
%token PLUS MINUS TIMES DIV EOF
%token LPAREN RPAREN
%token LEQ
%token TRUE FALSE
%token LET EQUALS IN
%token IF THEN ELSE 
%token FUNC ARROW%nonassoc IN
%nonassoc ELSE
%left LEQ
%left PLUS MINUS
%left TIMES DIV%start main
%type <Ast.expr> main
%%main:expr EOF { $1 }
;expr:| simpl_expr { $1 }| simpl_expr simpl_expr+ { make_apply $1 $2 }
;simpl_expr:| INT { Int $1 }| ID { Var $1 }| TRUE { Bool true }| FALSE { Bool false}| simpl_expr LEQ simpl_expr   { Binop (Leq, $1, $3) }| simpl_expr TIMES simpl_expr { Binop (Mul, $1, $3) }| simpl_expr DIV simpl_expr   { Binop (Div, $1, $3) }| simpl_expr PLUS simpl_expr  { Binop (Add, $1, $3) }| simpl_expr MINUS simpl_expr { Binop (Sub, $1, $3) }| LET ID EQUALS simpl_expr IN simpl_expr  { Let ($2, $4, $6) }| FUNC ID ARROW expr { Func ($2, $4) }| IF simpl_expr THEN simpl_expr ELSE simpl_expr { If ($2, $4, $6) }| LPAREN expr RPAREN { $2 }
;

4、lib/dune

(library(name Simpl_riscv)(modules parser lexer ast))(ocamllex lexer)
(menhir (modules parser))

5、bin/dune

(executable(public_name Simpl_riscv)(name main)(modules main)(libraries Simpl_riscv)(flags (:standard -w -32-27-26-39-8-37)))

6、bin/main.ml的部分代码

open Simpl_riscv
open Astlet rec string_of_expr (e : expr) : string = match e with| Int n -> Printf.sprintf "Int %d" n| Var id -> Printf.sprintf "Var %s" id| Bool b -> let b_str = match b with | true -> "true"| false -> "false"inPrintf.sprintf "Bool %s" b_str| Binop (binop, e1, e2) ->let binop_str = match binop with | Add -> "Add"| Mul -> "Mul"| Sub -> "Sub"| Div -> "Div"| Leq -> "Leq"inPrintf.sprintf "Binop (%s, %s, %s)" binop_str (string_of_expr e1) (string_of_expr e2)| Let (var, e1, e2) -> Printf.sprintf "Let (%s, %s, %s)" var (string_of_expr e1) (string_of_expr e2)| If (e1, e2, e3) -> Printf.sprintf "If (%s, %s, %s)" (string_of_expr e1) (string_of_expr e2) (string_of_expr e3)| Func (var, e) -> Printf.sprintf "Func (%s, %s)" var (string_of_expr e)| App (e1, e2) -> Printf.sprintf "App (%s, %s)" (string_of_expr e1) (string_of_expr e2)let parse s : expr =let lexbuf = Lexing.from_string s inlet ast = Parser.main Lexer.read lexbuf inast(* 全局标签计数器,用于生成唯一标签 *)
let label_count = ref 0
let fresh_label prefix = incr label_count;Printf.sprintf "%s_%d" prefix !label_count(* 全局列表:保存所有生成的函数代码,最终附加在程序末尾 *)
let functions : string list ref = ref [](* 简单的自由变量分析(不去重,仅适用于教学示例) *)
let rec free_vars expr bound = match expr with| Int _ | Bool _ -> []| Var x -> if List.mem x bound then [] else [x]| Binop (_, e1, e2) -> free_vars e1 bound @ free_vars e2 bound| Let (x, e1, e2) -> free_vars e1 bound @ free_vars e2 (x :: bound)| If (cond, e_then, e_else) ->free_vars cond bound @ free_vars e_then bound @ free_vars e_else bound| Func (x, body) -> free_vars body (x :: bound)| App (e1, e2) -> free_vars e1 bound @ free_vars e2 bound(*compile_expr env cur_offset exprenv: (variable, offset) 的关联列表,其中 offset 是相对于 fp 的偏移(单位:字节)cur_offset: 当前已经分配的 let 变量字节数(每个变量占 8 字节)返回的汇编代码保证计算结果存放在寄存器 a0 中
*)
let rec compiler_expr (env : (string * int) list) (cur_offset : int) (expr : expr) : string = match expr with| Int n -> Printf.sprintf "\tli a0, %d\n" n| Bool b ->if b then "\tli a0, 1\n" else "\tli a0, 0\n"| Var x ->(trylet offset = List.assoc x env inPrintf.sprintf "\tld a0, -%d(fp)\n" offsetwith Not_found ->failwith ("Unbound variable: " ^ x))
(*——————————————*)and compile_expr_func (local_env : (string * int) list) (closure_env : (string * int) list) (cur_offset : int) (expr : expr) : string =match expr with
(*——————————————*)let compiler_program (e : expr) : string =let body_code = compiler_expr [] 0 e inlet prologue = ".text\n\.global main\n\main:\n\\taddi sp, sp, -64\n\\tmv fp, sp\n"inlet epilogue = "\\tmv sp, fp\n\\taddi sp, sp, 64\n\\tret\n"inlet func_code = String.concat "\n" !functions inprologue ^ body_code ^ epilogue ^ "\n" ^ func_codelet () =let filename = "test/simpl_test4.in" in(* let filename = "test/simpl_test2.in" in *)let in_channel = open_in filename inlet file_content = really_input_string in_channel (in_channel_length in_channel) inclose_in in_channel;(* let res = interp file_content inPrintf.printf "Result of interpreting %s:\n%s\n\n" filename res;let res = interp_big file_content inPrintf.printf "Result of interpreting %s with big-step model:\n%s\n\n" filename res; *)let ast = parse file_content in Printf.printf "AST: %s\n" (string_of_expr ast);let output_file = Sys.argv.(1) inlet oc = open_out output_file inlet asm_code = compiler_program ast inoutput_string oc asm_code;close_out oc;Printf.printf "Generated RISC-V code saved to: %s\n" output_file

三、具体实现

1、bin/main.ml的compiler_expr函数

let rec compiler_expr (env : (string * int) list) (cur_offset : int) (expr : expr) : string = match expr with| Int n -> Printf.sprintf "\tli a0, %d\n" n| Bool b ->if b then "\tli a0, 1\n" else "\tli a0, 0\n"| Var x ->(trylet offset = List.assoc x env inPrintf.sprintf "\tld a0, -%d(fp)\n" offsetwith Not_found ->failwith ("Unbound variable: " ^ x))| Binop (op, e1, e2) ->let code1 = compiler_expr env cur_offset e1 in let push_left = "\taddi sp, sp, -8\n\tsd a0, 0(sp)\n" inlet code2 = compiler_expr env cur_offset e2 inlet pop_left = "\tld t0, 0(sp)\n\taddi sp, sp, 8\n" inlet op_code = match op with| Add -> "\tadd a0, t0, a0\n"| Sub -> "\tsub a0, t0, a0\n"| Mul -> "\tmul a0, t0, a0\n"| Div -> "\tdiv a0, t0, a0\n"| Leq -> "Not implemented"incode1 ^ push_left ^ code2 ^ pop_left ^ op_code| Let (x, e1, e2) ->let code1 = compiler_expr env cur_offset e1 inlet new_offset = cur_offset + 8 inlet alloc = Printf.sprintf "\taddi sp, sp, -8\n\tsd a0, -%d(fp)\n" new_offset inlet env' = (x, new_offset) :: env inlet code2 = compiler_expr env' new_offset e2 inlet free = "\taddi sp, sp, 8\n" incode1 ^ alloc ^ code2 ^ free| If (cond, e_then, e_else) ->let label_else = fresh_label "Lelse" inlet label_end = fresh_label "Lend" inlet code_cond = compiler_expr env cur_offset cond inlet code_then = compiler_expr env cur_offset e_then inlet code_else = compiler_expr env cur_offset e_else incode_cond ^ Printf.sprintf "\tbeq a0, x0, %s\n" label_else ^code_then ^Printf.sprintf "\tj %s\n" label_end ^Printf.sprintf "%s:\n" label_else ^code_else ^Printf.sprintf "%s:\n" label_end| Func (x, body) ->let fvs = free_vars body [x] inlet num_free = List.length fvs inlet func_id = fresh_label "func" inlet local_env = [(x, 8)] inlet closure_env = List.mapi (fun i v -> (v, 8 * i)) fvs inlet func_body_code = compile_expr_func local_env closure_env 0 body inlet func_prologue = Printf.sprintf "%s:\n\taddi sp, sp, -16\n\tsd ra, 8(sp)\n\tsd fp, 0(sp)\n\tmv fp, sp\n" func_idinlet func_epilogue = "\tld ra, 8(sp)\n\tld fp, 0(sp)\n\taddi sp, sp, 16\n\tret\n"inlet func_code = func_prologue ^ func_body_code ^ func_epilogue infunctions := !functions @ [func_code];let closure_size = 8 * (1 + num_free) inlet alloc_code = Printf.sprintf "\tli a0, %d\n\tjal ra, malloc\n" closure_size inlet move_closure = "\tmv t0, a0\n" inlet store_code_ptr = Printf.sprintf "\tla t1, %s\n\tsd t1, 0(t0)\n" func_id inlet store_free_vars = List.mapi (fun i v ->let outer_offset =try List.assoc v env with Not_found -> failwith ("Unbound free var: " ^ v)inPrintf.sprintf "\tld t1, -%d(fp)\n\tsd t1, %d(t0)\n" outer_offset (8 * (i + 1))) fvs |> String.concat ""inlet ret_code = "\tmv a0, t0\n" inalloc_code ^ move_closure ^ store_code_ptr ^ store_free_vars ^ ret_code| App (e1, e2) ->let code_f = compiler_expr env cur_offset e1 inlet save_closure = "\tmv t0, a0\n" inlet code_arg = compiler_expr env cur_offset e2 inlet load_env = "\taddi a1, t0, 8\n" inlet load_code_ptr = "\tld t1, 0(t0)\n" inlet call = "\tjalr ra, 0(t1)\n" incode_f ^ save_closure ^ code_arg ^ load_env ^ load_code_ptr ^ call

2、bin/main.ml的compile_expr_func函数

and compile_expr_func (local_env : (string * int) list) (closure_env : (string * int) list) (cur_offset : int) (expr : expr) : string =match expr with| Int n -> Printf.sprintf "\tli a0, %d\n" n| Bool b ->if b then "\tli a0, 1\n" else "\tli a0, 0\n"| Var x ->if List.mem_assoc x local_env thenPrintf.sprintf "\tld a0, -%d(fp)\n" (List.assoc x local_env)else if List.mem_assoc x closure_env thenPrintf.sprintf "\tld a0, %d(a1)\n" (List.assoc x closure_env)elsefailwith ("Unbound variable in function: " ^ x)| Binop (op, e1, e2) ->let code1 = compile_expr_func local_env closure_env cur_offset e1 inlet push_left = "\taddi sp, sp, -8\n\tsd a0, 0(sp)\n" inlet code2 = compile_expr_func local_env closure_env cur_offset e2 inlet pop_left = "\tld t0, 0, 0(sp)\n\taddi sp, sp, 8\n" inlet op_code = match op with| Add -> "\tadd a0, t0, a0\n"| Sub -> "\tsub a0, t0, a0\n"| Mul -> "\tmul a0, t0, a0\n"| Div -> "\tdiv a0, t0, a0\n"| Leq -> "Not implemented"incode1 ^ push_left ^ code2 ^ pop_left ^ op_code| If _ -> failwith "Not implemented"| Func _ | App _ -> failwith "Nested functions not supported in function bodies" 

 

相关文章:

简单程序语言理论与编译技术·22 实现一个从AST到RISCV的编译器

本文是记录专业课“程序语言理论与编译技术”的部分笔记。 LECTURE 22&#xff08;实现一个从AST到RISCV的编译器&#xff09; 一、问题分析 1、完整的编译器&#xff08;如LLVM&#xff09;需先完成AST到IR的转换&#xff0c;并进行代码优化&#xff0c;再到汇编&#xff0…...

Business English Certificates (BEC) 高频词汇学习

Business English Certificates {BEC} 高频词汇 References Cambridge English: Business Certificates, also known as Business English Certificates (BEC), are a suite of three English language qualifications for international business. abandon /əˈbndən/ vt. …...

lua和C的交互

1.C调用lua例子 #include <iostream> #include <lua.hpp>int main() {//用于创建一个新的lua虚拟机lua_State* L luaL_newstate();luaL_openlibs(L);//打开标准库/*if (luaL_dofile(L, "test.lua") ! LUA_OK) {std::cerr << "Lua error: &…...

Css:如何解决绝对定位子元素内容被父级元素overflow:hidden属性剪裁

一、问题描述 今天小伙伴提了一个bug&#xff0c;在点击列表项的“…”按钮应该出现的悬浮菜单显示不完整&#xff1a; 二、问题排查 一般这种问题&#xff0c;是由于悬浮菜单采用的是绝对定位&#xff0c;而父级采用了overflow:hidden属性。但需要注意的是&#xff0c;这里的…...

RoMo: Robust Motion Segmentation Improves Structure from Motion

前言 看起来像是一篇投稿CVPR的文章&#xff0c;不知道被哪个瞎眼审稿人拒了。同期还有一篇CVPR被接收的工作Segment Any Motion in Videos&#xff0c;看起来不如这篇直白&#xff08;也可能是因为我先看过spotlesssplats的缘故&#xff09;&#xff0c;后面也应该一并介绍了…...

MCP 极简入门 - 三分钟 Cline + Smithery 运行 time 服务

文章目录 一、&#x1f680; 初识Smithery&#xff1a;AI服务的新大陆找到心仪的服务 二、Cline 编辑配置文件&#x1f527;1、打开配置文件2. 添加Time Server配置3. 验证配置效果 三、&#x1f4ac; 实战对话&#xff1a;让AI告诉你时间四、服务管理小技巧&#x1f504;&…...

基本机动飞行性能

机动飞行时描述飞机在给定构型和发动机工作状态下改变飞行速度、飞行高度和飞行方向的能力 1. 水平加&#xff08;减&#xff09;速 水平加&#xff08;减&#xff09;速性能反映飞机在水平面内改变直线飞行速度的能力。描述水平加&#xff08;减&#xff09;速性能的参数包括…...

ES6 新特性全面总结

ES6 新特性全面总结 ES6(ECMAScript 2015)是JavaScript语言的重大更新&#xff0c;引入了许多强大的新特性&#xff0c;极大地提升了JavaScript的开发体验和能力。以下是ES6主要新增知识点的详细总结&#xff1a; (一)、ES6变量声明&#xff1a;let 和 const 详解 一、let 和…...

【Linux】进程间通信、匿名管道、进程池

一.什么是通信 进程间通信(Inter-Process Communication&#xff0c;IPC),是指在操作系统中&#xff0c;不同进程之间进行数据交换和同步的机制。由于每个进程通常拥有独立的内存空间&#xff0c;进程间无法直接访问对方的内存&#xff0c;因此需要通过特定的机制来实现通信和…...

【HTML】纯前端网页小游戏-戳破彩泡

分享一个简单有趣的网页小游戏 - 彩色泡泡爆破。玩家需要点击屏幕上随机出现的彩色泡泡来得分。 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…...

【MATLAB定位例程】TDOA(到达时间差)的chan-tylor,三维环境,附完整代码

该代码实现了基于三维空间的动态目标TDOA定位,结合了Chan算法(解析解)与Taylor级数展开法(迭代优化)的双重优势。 文章目录 运行结果MATLAB代码代码讲解代码功能概述核心算法原理代码结构解析可视化与结果分析运行结果 定位示意图: 三轴状态曲线: 三轴误差曲线: MA…...

数字化转型中的开源AI智能客服与S2B2C商城小程序的融合创新

摘要 数字经济时代&#xff0c;企业需通过技术重构用户交互与供应链体系。本文以“开源AI智能客服”“AI智能名片”及“S2B2C商城小程序”为核心&#xff0c;研究三者如何通过技术协同与场景化应用实现企业营销、客户服务与供应链管理的智能化升级。通过案例分析、技术架构设…...

重生之我是去噪高手——diffusion model

diffusion model是如何运作的&#xff1f; 想象一下&#xff0c;你有一张清晰的图片。扩散模型的核心思想分为两个过程&#xff1a; 前向过程&#xff08;Forward Process / Diffusion Process&#xff09;&#xff1a;逐步加噪反向过程&#xff08;Reverse Process / Denois…...

【C#】.net core 6.0 依赖注入常见问题之一,在构造函数使用的类,都需要注入到容器里,否则会提示如下报错,让DeepSeek找找原因,看看效果

&#x1f339;欢迎来到《小5讲堂》&#x1f339; &#x1f339;这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。&#x1f339; &#x1f339;温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01;&#…...

论文阅读笔记——RDT-1B: A DIFFUSION FOUNDATION MODEL FOR BIMANUAL MANIPULATION

RDT-1B 论文 模型表达与泛化能力&#xff1a;由于双臂操作中动作空间维度是单臂空间的两倍&#xff0c;传统方法难以建模其多模态分布。 数据&#xff1a;双臂数据少且不同机器人的物理结构和动作空间差异&#xff08;如关节数、运动范围&#xff09;导致数据分布不一致&#x…...

Vue中将pdf文件转为图片

平时开发中,我们经常遇到的场景应该是调用后端接口返回给前端pdf格式的文件流,然后我们可以通过URL.createObjectURL的方式转为object url临时路径然后可以通过window.open的方式来打开一个新的浏览器页签来进行预览,效果如下图: 但有时候这样满足不了的需求,它不想这样预…...

day39——输入操作:多值输入

数组输入&#xff1a; int main() {//***** 1、多值输入&#xff08;C&#xff09;/*输入&#xff1a;3 --> 3个值5 4 9*/int n;cin >> n; //输入个数const int MAX_SIZE 0xFFFF;//限定最大个数int a[MAX_SIZE];for (int i 0; i < n; i) {//用 n 作控制输入…...

微软的 Copilot 现在可以浏览网页并为您执行操作

在庆祝其 50 岁生日之际&#xff0c;微软正在向其人工智能驱动的 Copilot 聊天机器人传授一些新技巧。 从 BASIC 到 AI&#xff0c;改变世界的公司&#xff1a;微软 微软表示&#xff0c;Copilot 现在可以在“大多数网站”上采取行动&#xff0c;使其能够预订门票、预订餐厅等…...

elasticsearch 7.17 索引模板

文章目录 概要 概要 模板 import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.introns.framework.es.builder.OperationsBuilder; import java.util.HashMap; import java.util.Map;abstract class AbstractBuilder<T extends Abstrac…...

深入理解Python元组:从基础到高级应用

1. 元组基础认知 1.1 什么是元组 不可变序列&#xff1a;元组(tuple)是Python内置的不可变序列类型异构容器&#xff1a;可以存储不同类型的数据&#xff08;与列表类似&#xff09;语法特征&#xff1a;使用圆括号()定义&#xff0c;元素间用逗号分隔 # 基本示例 t1 (1, 2…...

【零基础入门unity游戏开发——动画篇】unity旧动画系统Animation组件的使用

考虑到每个人基础可能不一样&#xff0c;且并不是所有人都有同时做2D、3D开发的需求&#xff0c;所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】&#xff1a;主要讲解C#的基础语法&#xff0c;包括变量、数据类型、运算符、…...

Python+AI提示词用贝叶斯样条回归拟合BSF方法分析樱花花期数据模型构建迹图、森林图可视化

原文链接&#xff1a;https://tecdat.cn/?p41308 在数据科学的领域中&#xff0c;我们常常会遇到需要处理复杂关系的数据。在众多的数据分析方法中&#xff0c;样条拟合是一种非常有效的处理数据非线性关系的手段。本专题合集围绕如何使用PyMC软件&#xff0c;对樱花花期数据进…...

记一个.NET AOT交叉编译时的坑

记一个.NET AOT交叉编译时的坑 背景&#xff1a; 使用.NET9开发的Avalonia项目需要部署到Linux-arm64 踩坑&#xff1a; 根据官方AOT交叉编译文档配置后执行打包 dotnet publish -r linux-arm64提示error : The PrivateSdkAssemblies ItemGroup is required for _ComputeA…...

消息中间件对比与选型指南:Kafka、ActiveMQ、RabbitMQ与RocketMQ

目录 引言 消息中间件的定义与作用 消息中间件在分布式系统中的重要性 对比分析的四种主流消息中间件概述 消息中间件核心特性对比 消息传递模型 Kafka&#xff1a;专注于发布-订阅模型 ActiveMQ&#xff1a;支持点对点和发布-订阅两种模型 RabbitMQ&#xff1a;支持点…...

实战打靶集锦-38-inclusiveness

文章目录 1. 主机发现2. 端口扫描&服务枚举3. 服务探查4.系统提权 靶机地址&#xff1a;https://download.vulnhub.com/inclusiveness/Inclusiveness.ova 1. 主机发现 目前只知道目标靶机在192.168.56.xx网段&#xff0c;通过如下的命令&#xff0c;看看这个网段上在线的主…...

JVM 学习计划表(2025 版)

JVM 学习计划表&#xff08;2025 版&#xff09; &#x1f4da; 基础阶段&#xff08;2 周&#xff09; 1. JVM 核心概念 ​JVM 作用与体系结构 理解 JVM 在 Java 跨平台运行中的核心作用&#xff0c;掌握类加载子系统、运行时数据区、执行引擎的交互流程​内存结构与数据存…...

Unhandled exception: org.apache.poi.openxml4j.exceptions.InvalidFormatException

代码在main方法里面没有报错&#xff0c;在Controller里面就报错了。 原来Controller类里面少了行代码 import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 加上去就解决了。...

Java的Selenium元素定位-xpath

xpath其实就是一个path(路径)&#xff0c;一个描述页面元素位置信息的路径&#xff0c;相当于元素的坐标xpath基于XML文档树状结构&#xff0c;是XML路径语言&#xff0c;用来查询xml文档中的节点。 绝对定位 从根开始找--/(根目录)/html/body/div[2]/div/form/div[5]/button缺…...

【QT】Qt5 QtWebEngine使用教程

目录 1、QtWebEngine相比于QtWebKit的优势2、项目配置2.1 确认 Qt 版本2.2 在.pro 文件中添加依赖3、显示网页4、实现Qt和网页JavaScript之间的交互4.1 Qt执行网页的JavaScript代码4.2 JavaScript调用Qt对象的函数QtWebEngine 是 Qt 框架中用于在应用程序中嵌入 Web 内容的模块…...

python基础-13-处理excel电子表格

文章目录 【README】【13】处理Excel电子表格【13.1】Excel文档【13.2】安装openpyxl模块【13.3】读取Excel文档【13.3.1】使用openpyxl模块打开excel文档【13.3.2】从工作簿取得工作表【13.3.3】从工作表sheet获取单元格cell【13.3.5】从表中获取行和列【13.3.6】工作簿、工作…...