golang qmk complier - golang

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang qmk complier - golang相关的知识,希望对你有一定的参考价值。

package main

import (
	"Goose/conf"
	"Goose/router"
	"bytes"
	"database/sql"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"golang.org/x/crypto/bcrypt"

	"github.com/echo-contrib/sessions"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	_ "github.com/mattn/go-sqlite3"
	"github.com/syntaqx/renderer"
	"github.com/unrolled/render"
)

// Response 返回的json结构
//http://stackoverflow.com/questions/942951/rest-api-error-return-good-practices
type Response struct {
	Version string      `json:"version"` //api version
	Status  string      `json:"status"`  // http always =200,the real err will be here
	Msg     string      `json:"msg"`     // for human
	Data    interface{} `json:"data"`
}

func main() {

	qmkRoot := conf.GetOption("QMK", "root")
	db, err := sql.Open("sqlite3", conf.GetOption("system", "db"))
	if err != nil {
		fmt.Println(err)
	}
	e := echo.New()
	// Enable debug logging
	e.Debug = true

	// Keeps the DefaultFuncs provided by renderer
	funcs := []template.FuncMap{renderer.DefaultFuncs}

	// Create an instance of unrolled/render with app-specific configurations.
	r := render.New(render.Options{
		//Layout:        "layout",
		Directory:     "templates",
		Extensions:    []string{".html"},
		IsDevelopment: e.Debug,
		Funcs:         funcs,
	})

	// Wrap the render instance with the Renderer compliant interface.
	e.Renderer = renderer.Wrap(r)

	// Attach middlewares
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	e.Static("/static", "static")                //static file in /static
	e.File("/favicon.ico", "static/favicon.ico") //Serve favicon.ico

	store := sessions.NewCookieStore([]byte("secret"))
	if err != nil {
		panic(err)
	}
	e.Use(sessions.Sessions("echosession", store))

	// Let's give it a go!
	e.GET("/", func(c echo.Context) error {
		session := sessions.Default(c)
		var value string
		val := session.Get("name")
		if val == nil {
			c.Redirect(301, "/login")
		} else {
			c.Redirect(301, "/mitosis-plus")
		}

		return c.JSON(200, value)
	})
	e.GET("/api/logout", func(c echo.Context) error {
		session := sessions.Default(c)
		session.Clear()
		session.Save()
		return c.Redirect(301, "/")
	})
	e.POST("/api/login", func(c echo.Context) error {
		name := c.FormValue("name")
		pwd := c.FormValue("pwd")
		var Password string
		err := db.QueryRow("select Password from user where Activity='1' and UserName = $1", name).Scan(&Password)
		if err != nil {
			fmt.Println(err)
		}
		// nil means it is a match
		bHashedPwd := []byte(Password)
		bPwd := []byte(pwd)
		err = bcrypt.CompareHashAndPassword(bHashedPwd, bPwd)
		// 密码正确
		if err == nil {
			session := sessions.Default(c)
			session.Set("name", name)
			session.Save()
			u := &Response{
				Version: "0.1",
				Status:  "301",
				Msg:     "Login successful.",
				Data:    "/mitosis-plus",
			}
			return c.JSON(http.StatusOK, u)
		}
		// 密码不对
		u := &Response{
			Version: "0.1",
			Status:  "401",
			Msg:     "Wrong pwd",
			Data:    "The User Name or Password is incorrect.",
		}
		return c.JSON(http.StatusOK, u)
	})
	e.POST("/api/signup", router.SignUp)
	e.GET("/login", func(c echo.Context) error {
		return c.Render(http.StatusOK, "login", "")
	})

	e.GET("/mitosis-plus", func(c echo.Context) error {
		return c.Render(http.StatusOK, "kb", "")
	})

	e.POST("/make/mitosis-plus", func(c echo.Context) error {
		layer0 := c.FormValue("layer0")
		layer1 := c.FormValue("layer1")
		layer2 := c.FormValue("layer2")
		layer3 := c.FormValue("layer3")
		layer4 := c.FormValue("layer4")
		qmkABS, _ := filepath.Abs(qmkRoot)
		//keymapHead, err := ioutil.ReadFile(qmkABS + "/keyboards/mitosis/keymaps/defult/head.c")
		f, err := os.Open(qmkABS + "/keyboards/mitosis/keymaps/default/head.c")
		if err != nil {
			u := &Response{
				Version: "0.1",
				Status:  "400",
				Msg:     "服务器内部错误",
				Data:    err,
			}
			return c.JSON(http.StatusOK, u)
		}
		keymapHead, err := ioutil.ReadAll(f)

		f, err = os.Open(qmkABS + "/keyboards/mitosis/keymaps/default/foot.c")
		if err != nil {
			u := &Response{
				Version: "0.1",
				Status:  "400",
				Msg:     "服务器内部错误",
				Data:    err,
			}
			return c.JSON(http.StatusOK, u)
		}
		keymapFoot, err := ioutil.ReadAll(f)
		f, err = os.Open(qmkABS + "/keyboards/mitosis/keymaps/default/no.c")
		if err != nil {
			u := &Response{
				Version: "0.1",
				Status:  "400",
				Msg:     "服务器内部错误",
				Data:    err,
			}
			return c.JSON(http.StatusOK, u)
		}
		keymapNo, err := ioutil.ReadAll(f)

		if layer0 == "" {
			u := &Response{
				Version: "0.1",
				Status:  "400",
				Msg:     "请至少设置一层",
				Data:    "Layer0 undefined.",
			}
			return c.JSON(http.StatusOK, u)
		}
		if layer1 == "" || layer1 == "undefined" {
			layer1 = string(keymapNo)
		}
		if layer2 == "" || layer2 == "undefined" {
			layer2 = string(keymapNo)
		}
		if layer3 == "" || layer3 == "undefined" {
			layer4 = string(keymapNo)
		}
		if layer4 == "" || layer4 == "undefined" {
			layer4 = string(keymapNo)
		}
		layer0 = "[0] = {\n" + layer0 + "\n},\n"
		layer1 = "[1] = {\n" + layer1 + "\n},\n"
		layer2 = "[2] = {\n" + layer2 + "\n},\n"
		layer3 = "[3] = {\n" + layer3 + "\n},\n"
		layer4 = "[4] = {\n" + layer4 + "\n},\n"
		u := &Response{
			Version: "0.1",
			Status:  "200",
			Msg:     layer1,
			Data:    keymapHead,
		}
		buf := bytes.NewBuffer(keymapHead)
		buf.Write([]byte(layer0))
		buf.Write([]byte(layer1))
		buf.Write([]byte(layer2))
		buf.Write([]byte(layer3))
		buf.Write([]byte(layer4))
		buf.Write([]byte(keymapFoot))
		t := fmt.Sprint(time.Now().UnixNano())
		tmpPath := qmkABS + "/keyboards/mitosis/keymaps/" + t
		fmt.Println(tmpPath)
		err = os.MkdirAll(tmpPath, 0711)
		err = ioutil.WriteFile(tmpPath+"/keymap.c", buf.Bytes(), 0644)
		defer f.Close()

		cmd := exec.Command("/bin/bash", "-c", `cd '`+qmkABS+`';make mitosis-`+t)
		//创建获取命令输出管道
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			fmt.Printf("Error:can not obtain stdout pipe for command:%s\n", err)
			return err
		}

		//执行命令
		if err := cmd.Start(); err != nil {
			fmt.Println("Error:The command is err,", err)
			return err
		}

		//读取所有输出
		bytes, err := ioutil.ReadAll(stdout)
		if err != nil {
			fmt.Println("ReadAll Stdout:", err.Error())
			return err
		}

		if err := cmd.Wait(); err != nil {
			fmt.Println("wait:", err.Error())
			return err
		}
		//移动编译文件
		fmt.Printf("stdout:\n\n %s", bytes)
		err = os.Rename(qmkABS+"/mitosis_"+t+".hex", "static/hexfiles/mitosis_"+t+".hex")
		u = &Response{
			Version: "0.1",
			Status:  "200",
			Msg:     "make",
			Data:    bytes,
		}
		err = os.RemoveAll(tmpPath)
		if err != nil {
			return c.JSON(http.StatusOK, u)
		}
		return c.Redirect(301, "/static/hexfiles/mitosis_"+t+".hex")
	})

	// RESTful api
	e.GET("/api", func(c echo.Context) error {
		u := &Response{
			Version: "0.1",
			Status:  "200",
			Msg:     "ok",
			Data:    "Oh hello, sir! -J.A.R.V.I.S",
		}
		return c.JSON(http.StatusOK, u)
	})

	// port := cfg.Section("system").Key("port").String()
	e.Logger.Fatal(e.Start(conf.GetOption("system", "port")))
}

vue3源码分析——看看complier是怎么来解析的

引言

<<往期回顾>>

  1. vue3源码分析——手写diff算法

  2. vue3源码分析——实现组件更新

  3. vue3源码分析——解密nextTick的实现

想知道vue3-complier是怎么实现的吗?🤔🤔🤔,本期就来实现vue3-complier的基础,看看·vue是如果来处理模板的,所有的源码请查看仓库

正文

效果

astexplorer.net/ 这个网站上可以写上vue的模板,然后选择vue3-complier,就可以得到vue3编译模板后的ast了。 最终效果如下:

但是对于一开始肯定是没有那么多属性的,所以可以小步走。先来实现个最简单的形式。

// 输入 <div>hi twinkle, message</div>

// 输出对应的ast树


  "type": 0,
  "children": [
    "type": 1,
    "tag": "div",
    "children": [
      "type": 2,
      "content": "hi, twinkle, "
    ,
    
      "type": 5,
      "content": 
        "type": 4,
        "content": "message"
      
    
    ]
  ]


看到这个ast, vue是自己手动实现了一个complier,也就是说手动实现了ast解析,下面就一起来看看吧!😉😉😉

分步走

<div>hi twinkle, message</div>中这可以可以分成三种类型的节点。

  • element: 解析div类型, 如<div></div>
  • text: 解析文本类型, 如hi, twinkle
  • interpolation(插值): 解析插值类型,如message

既然可以分为三种,那就对外提供一个公共的解析方法,然后在内部来分化成其他的情况。但是对外是一个方法,这里肯定会用到循环,循环调用那个解析字符串的方法,如下图:

根据上面的这副图,可以写出以下的代码

export function baseParse(str)
 // 为了方便处理str,使用一个引用值来存储str
 cosnt context = source: str
 const children = parseChildren(context)
 return type: 0, children


function parseChildren(context)
  const str = context.source
    const nodes = []
    let node;
    // 判断类型
    if(s.startsWidth(''))
      // 解析插值
    node =  parseInterpolation(context)
    else if(s.startsWidth('<') && /<[a-z]*>/i).test(str))
      // 解析元素
    node =  parseElement(context)
    else
      // 解析文本
    node =   parseText(context)
    
    
    if(node)
      nodes.push(node)
    
    
    return nodes;


这里还会有一个问题,那就是怎么来处理传入的str, vue采用的方式是:稳扎稳打,逐步推进,意思是说,对里面的每一种类型进行判断,进行对应的逻辑处理,处理完的把字符串给删除掉(推进

有了上面的分发,那么接下来可以对每一个方法进行具体的实现

实现解析Element

element的格式是<div>...</div>,必须是<开头的,而且后面一定会有>符号结尾,那就能够拿到标签的tag。

function parseElement(context)
 const s = context.source
 const elemet = type: 1
 // 先拿到tag,并且把<div>给移除掉
   const match = /<\\/?([a-z]*)>/i.exec(context.source)
   // match的结构是 ['<div>', 'div', ...]
   if (match && match.length > 1) 
      elemet.tag = match[1]
      // 移除掉<div>
      context.source = s.slice(match[0].length)
   
   // 把头去掉后,剩余中间的内容,有文本和插值,需要重新调用parseChildren
   elemet.children = parseChildren(context)
   // 处理完中间部分,需要把</div>给移除掉
   const matchEnd =  /<\\/?([a-z]*)>/i.exec(context.source)
     if (matchEnd && matchEnd.length > 1) 
      // 移除掉<div>
      context.source = s.slice(matchEnd[0].length)
   
   return element


处理element来说,只要找出头部的标签,就调用patchChildren来处理内部的内容,然后处理结束标签,这个理解起来应该不是很难

处理Text

处理文本类型的关键在于判断文本到哪里截止,也就是文本的长度是多少。这里的话需要判断文本里面这个符号的位置即可。

function parseText(context)
  // 先让index是文本的长度,然后来找满足条件的其他位置
  const s = context.source.length;
  let index = s.length;
  const findIndex = s.indexOf('')
  // 如果找到了
  if(findIndex > -1)
    index = findIndex
  
  // 拿到content,当前的文本内容
  const content = s.slice(0, index)
  // 对文本进行截取
  context.source = s.slice(index)
  return 
    type: 2,
    content
  
  
  // 需要继续处理,这里调用patchChildren?
  // 显然不是,renturn后面的代码并不会执行


处理text是不难的,只需要判断文本中有没有特殊符号,如果有的话,那就找到位置,处理文本到特殊符号当前。然后继续调用patchChildren方法.但是这里有一个问题,处理完文本都return了,怎么调用patchChildren方法呢?

办法肯定有的,只需要在patchChildrenwhile循环即可,但是需要判断结束条件哦!

改造parseChildren方法


function parseChildren(context)
  const str = context.source
    const nodes = []
    // 当长度解析完毕后就结束
    while(!context.source.length)
    // ……原有的逻辑不变
    
    
    return nodes;


解析插值

上面的字符串经过前两步的解析,最终变成 message</div>,接下来实现最后的解析插值

function parseInterpolation(context)
    const s = context.source;
    // 去掉
    context.source = s.slice(2)
    // 找到 的位置
    const end = context.source.indexOf('',2);
    // 获取中间的内容
    const content = s.slice(2, end);
    // 去掉
    context.source = context.source.slice(end+2)
    // 返回结果
    return 
     "type": 5,
      "content": 
        "type": 4,
        content
      
    


如此就可以解析插值啦,接下里就可以解析完成,然后获取对应的ast树!😀😀😀

由于篇幅关系,这里就分析核心,我在complier basic中解析了更多的情况,有兴趣的可以搞起来哦!!!

总结

本期主要实现了vue3中的complier的核心,是如何<div>hi, twinkle, message</div>这里字符串的。vue3是采用逐步推进的方式,解析完一段就删除一段,然后传给后续的方法来继续解析.在解析的过程中就是主要考验js对字符串处理的实力了👍👍👍

以上是关于golang qmk complier - golang的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp qmk_firmware的最小工作示例保持+点击双键修改

vue3源码分析——看看complier是怎么来解析的

vue3源码分析——看看complier是怎么来解析的

vue3源码分析——看看complier是怎么来解析的

vue3源码分析——看看complier是怎么来解析的

java complier compliance level问题引发的思考