1 Star 1 Fork 1

U语言组织/U语言

forked from 秋来冬风/U语言 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
main.go 11.13 KB
一键复制 编辑 原始数据 按行查看 历史
package main
import (
"context"
"errors"
"fmt"
"io/fs"
"os"
"os/exec"
"path/filepath"
"runtime/debug"
"strings"
"time"
"gitee.com/u-language/u-language/ucom/ast"
"gitee.com/u-language/u-language/ucom/ast2"
"gitee.com/u-language/u-language/ucom/cast"
"gitee.com/u-language/u-language/ucom/cast2"
"gitee.com/u-language/u-language/ucom/config"
"gitee.com/u-language/u-language/ucom/errcode"
"gitee.com/u-language/u-language/ucom/format"
idebug "gitee.com/u-language/u-language/ucom/internal/debug"
"gitee.com/u-language/u-language/ucom/internal/errutil"
"gitee.com/u-language/u-language/ucom/internal/utils"
"gitee.com/u-language/u-language/ucom/ir2"
"gitee.com/u-language/u-language/ucom/parser"
"github.com/spf13/cobra"
)
var (
build string //编译地址
mode string //模式
out string //输出文件名
Thread bool = true
cok int
err error
buildmode int // if ==1 lex -> ast , if==2 lex2 -> ast2
buildCmd = &cobra.Command{
Use: "build",
Short: "编译源文件地址",
Long: "编译源文件地址",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cmd.PrintErr("build 支持处理的不是一个源文件就是一个目录下的所有.u源文件")
os.Exit(2)
}
build = args[0]
defer Recover()
Build(build, out, buildmode)
},
}
donectx context.Context
cancelfunc context.CancelFunc
rootcmd = &cobra.Command{
Use: "",
Short: "U语言编译器",
}
build2Cmd = &cobra.Command{
Use: "build2",
Short: "编译源文件地址",
Long: "编译源文件地址",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cmd.PrintErr("build 支持处理的不是一个源文件就是一个目录下的所有.u源文件")
os.Exit(2)
}
build = args[0]
defer Recover()
Build2(build, out)
},
}
)
func init() {
donectx, cancelfunc = context.WithCancel(context.Background())
out, err = filepath.Abs(out)
utils.MustErr(err)
rootcmd.AddCommand(buildCmd)
rootcmd.AddCommand(versionCmd)
rootcmd.AddCommand(fmtCmd)
rootcmd.AddCommand(build2Cmd)
buildFlag(buildCmd)
buildFlag(build2Cmd)
//报错
go errcode.Handle(donectx, func(err errcode.ErrInfo) { fmt.Println(err.String()) }, func() { os.Exit(2) })
buildCmd.Flags().IntVar(&buildmode, "buildmode", 1, "设置编译模式")
}
func buildFlag(c *cobra.Command) {
c.Flags().BoolVarP(&Thread, "p", "p", true, "是否使用多核")
c.Flags().StringVarP(&mode, "mode", "m", "c", `目标文件类型
c : c语言文件
fasm : fasm汇编文件(实验性的,目前暂停开发)`)
c.Flags().StringVar(&out, "o", "a"+utils.Ext, "输出文件名")
c.Flags().CountVarP(&cok, "c", "c", "是否只输出C文件")
}
func main() {
defer Recover()
if idebug.Debug { //调试时操作
fmt.Println("Thread:", Thread)
idebug.TraceStart()
idebug.PprofStart()
}
err = rootcmd.Execute()
if err != nil {
rootcmd.Help()
os.Exit(2)
return
}
}
// SaveCFile 将C抽象语法树表示的C源代码保存到文件中,并调用C工具链编译
func SaveCFile(toc *cast.UtoC, file string, outfile string) bool {
tmp, err := os.MkdirTemp("", "c*")
utils.MustErr(err)
file = filepath.Join(tmp, file)
fd, err := os.Create(file)
utils.MustErr(err)
_, err = fd.WriteString(toc.C())
utils.MustErr(err)
fd.Close()
clist := append([]string(nil), file)
if toc.InAutoFree {
clist = append(clist, utils.StdOne["mempool"].CFile...)
}
if _CCBuild(tmp, clist, outfile) { //调用C工具链生成可执行文件
utils.MustErr(os.RemoveAll(tmp))
return true
}
fmt.Println("C文件在", tmp)
return false
}
// _CCBuild 调用C工具链将file编译为
func _CCBuild(tmp string, clist []string, outfile string) bool {
if cok != 0 {
fmt.Println("C文件在", tmp)
os.Exit(0)
}
CCall := strings.Split(config.CC, "||")
for _, cc := range CCall {
arg := append([]string(nil), clist...)
arg = append(arg, "-lpthread", "-o", filepath.Join(tmp, "a"+utils.Ext), "-I")
arg = append(arg, utils.StdOne["mempool"].HDir...)
if Exec(cc, arg) {
utils.MustErr(utils.MoveFile(filepath.Join(tmp, "a"+utils.Ext), outfile))
return true
}
}
fmt.Println("尝试从所有c工具链生成二进制文件都失败了,没有删除c源文件")
return false
}
var outputPrint = false
func Exec(path string, args []string) bool {
cmd := exec.Command(path)
cmd.Args = append(cmd.Args, args...)
str, err := cmd.CombinedOutput()
if outputPrint && err != nil {
fmt.Println(string(str))
}
if err == nil {
return true
} else {
var exiterr *exec.ExitError
if errors.As(err, &exiterr) {
fmt.Printf("调用C工具链 %s 生成二进制失败,退出值=%d\n", path, exiterr.ExitCode())
} else if errors.Is(err, exec.ErrNotFound) {
fmt.Printf("C工具链 %s 没找到\n", path)
} else if errors.Is(err, fs.ErrNotExist) {
fmt.Printf("C工具链 %s 没找到\n", path)
}
}
return false
}
func FindC(dir string) []string {
var ret = make([]string, 0)
err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() && path != dir {
return filepath.SkipDir
}
if filepath.Ext(info.Name()) == ".c" {
ret = append(ret, path)
}
return nil
})
utils.MustErr(err)
return ret
}
func Recover() {
if idebug.Debug { //调试时操作
idebug.PprofClose()
idebug.TraceClose()
}
if err := recover(); err != nil {
if errv, ok := err.(error); ok {
switch {
case errors.Is(errv, os.ErrNotExist): //如果文件不存在
fmt.Println(build, "不存在")
os.Exit(2)
case errutil.IsCompileErr(errv): //如果是编译器错误
debug.PrintStack()
fallthrough
case errors.Is(errv, parser.NoUFileErr), errors.Is(errv, parser.ImportLimit), errors.Is(errv, parser.FileNoExt), errors.Is(errv, parser.PathNotFound): //如果是预定义错误
fmt.Println(errv.Error())
os.Exit(2)
case errors.Is(errv, format.ErrorAfterLex):
cancelfunc()
return
}
}
if idebug.Debug {
fmt.Println(err)
debug.PrintStack()
} else {
fmt.Println("发生不能识别的错误")
}
os.Exit(2)
}
}
// SaveCFileBuildMode2 将C语言转换器表示的C源代码保存到文件中,并调用C工具链编译
func SaveCFileBuildMode2(toc *cast2.UtoC, file string, outfile string) bool {
tmp, err := os.MkdirTemp("", "c*")
utils.MustErr(err)
file = filepath.Join(tmp, file)
fd, err := os.Create(file)
utils.MustErr(err)
_, err = fd.WriteString(toc.C(true))
utils.MustErr(err)
fd.Close()
clist := append([]string(nil), file)
// if toc.InAutoFree {
// clist = append(clist, utils.StdOne["mempool"].CFile...)
// }
if _CCBuild(tmp, clist, outfile) { //调用C工具链生成可执行文件
utils.MustErr(os.RemoveAll(tmp))
return true
}
fmt.Println("C文件在", tmp)
return false
}
// Build 将U代码构建
// - path是U代码路径
// - out是输出路径
// - buildmode是编译模式
func Build(path string, out string, buildmode int) {
if buildmode == 2 {
tree, err := parser.ParserAutoBuildMode2(path, Thread, errcode.DefaultErrCtx)
utils.MustErr(err)
if errcode.Errbol() { //代码有错误
cancelfunc()
time.Sleep(1000 * time.Second) //代码有错误不应继续执行
}
if tree == nil { //如果是空文件
return
}
switch mode {
case "c": //c模式
switch tree := tree.(type) {
case *ast2.Tree:
toc := &cast2.UtoC{}
toc.Parser(tree) //转换为C抽象语法树
cancelfunc()
if idebug.Debug && config.Debug_PrintCast == "on" { //调试时操作
fmt.Println(toc.C(true))
} else {
SaveCFileBuildMode2(toc, "out.c", out)
}
case *ast2.Package:
p := cast2.NewPackage(Thread, tree, true)
if idebug.Debug && config.Debug_PrintCast == "on" { //调试时操作
fmt.Printf("%+v\n", p)
} else {
tmp, err := os.MkdirTemp("", "c*")
utils.MustErr(err)
paths, err := p.OupputC(tmp)
clist := []string{paths}
utils.MustErr(err)
// if p.WithAutoFree() {
// paths = append(paths, utils.StdOne["mempool"].CFile...)
// }
if _CCBuild(tmp, clist, out) {
utils.MustErr(os.RemoveAll(tmp))
} else {
fmt.Println("C文件在", tmp)
}
}
}
case "fasm": //fasm模式
panic(errutil.NewErr2(errutil.CompileErr, "build --buildmode=2 不支持fasm"))
}
return
}
//解析获得抽象语法树,并进行语义检查
iface, err := parser.ParserAuto(path, Thread, errcode.DefaultErrCtx)
utils.MustErr(err)
if errcode.Errbol() { //代码有错误
cancelfunc()
time.Sleep(1000 * time.Second) //代码有错误不应继续执行
}
if utils.IsNil(iface) { //如果是空文件
return
}
cancelfunc()
switch mode {
case "c": //c模式
switch iface.(type) {
case *ast.Tree:
toc := cast.NewUtoC()
ast := iface.(*ast.Tree)
toc.Parser(ast, false) //转换为C抽象语法树
if idebug.Debug && config.Debug_PrintCast == "on" { //调试时操作
fmt.Printf("%+v\n", toc.C())
} else {
SaveCFile(toc, "out.c", out)
}
case *ast.Package:
p := cast.NewPackage(Thread)
astp := iface.(*ast.Package)
p.AddUastSlice(astp, nil)
if idebug.Debug && config.Debug_PrintCast == "on" { //调试时操作
fmt.Printf("%+v\n", p)
} else {
tmp, err := os.MkdirTemp("", "c*")
utils.MustErr(err)
paths, err := p.OupputC(tmp)
utils.MustErr(err)
if p.WithAutoFree() {
paths = append(paths, utils.StdOne["mempool"].CFile...)
}
if _CCBuild(tmp, paths, out) {
utils.MustErr(os.RemoveAll(tmp))
} else {
fmt.Println("C文件在", tmp)
}
}
}
case "fasm": //fasm模式
CallAmd64(iface.(*ast.Tree))
}
}
// Build2 将U代码构建,使用ir2
// - path是U代码路径
// - out是输出路径
func Build2(path string, out string) {
//解析获得ir,并进行语义检查
result, err := parser.ParserAuto2(path, Thread, errcode.DefaultErrCtx)
if err != nil {
if err.Error() != "11" { //如果是未知的错误
cancelfunc()
panic(err)
} else { //如果是代码解析完抽象语法树发现有错误
cancelfunc()
return
}
}
cancelfunc()
switch mode {
case "c":
switch result.(type) {
case *ir2.File:
f := result.(*ir2.File)
toc := cast.NewIr2ToC(f.PackageName, f.HaveInitFunc)
cast.PaeserIr2(toc, f) //转换为C抽象语法树
if idebug.Debug && config.Debug_PrintCast == "on" { //调试时操作
fmt.Printf("%+v\n", toc.C())
} else {
SaveCFile2(toc, "out.c", out)
}
}
case "fasm": //fasm模式
panic(errutil.NewErr2(errutil.CompileErr, "build2不支持fasm"))
}
}
// SaveCFile 将C抽象语法树表示的C源代码保存到文件中,并调用C工具链编译
func SaveCFile2(toc *cast.Ir2ToC, file string, outfile string) bool {
tmp, err := os.MkdirTemp("", "c*")
utils.MustErr(err)
file = filepath.Join(tmp, file)
fd, err := os.Create(file)
utils.MustErr(err)
_, err = fd.WriteString(toc.C())
utils.MustErr(err)
fd.Close()
clist := append([]string(nil), file)
// if toc.InAutoFree {
// clist = append(clist, utils.StdOne["mempool"].CFile...)
// }
if _CCBuild(tmp, clist, outfile) { //调用C工具链生成可执行文件
utils.MustErr(os.RemoveAll(tmp))
return true
}
fmt.Println("C文件在", tmp)
return false
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/u-language/u-language.git
git@gitee.com:u-language/u-language.git
u-language
u-language
U语言
master

搜索帮助