package emit import ( "git.smesh.lol/moxie/pkg/syntax" "git.smesh.lol/moxie/pkg/token" "git.smesh.lol/moxie/pkg/types" "runtime" "unsafe" ) var irResults []string var ImportRegistry map[string]*types.TCPackage func EnsureImportRegistry() { if ImportRegistry == nil { ImportRegistry = map[string]*types.TCPackage{} } } //export moxie_compile_to_ir func moxie_compile_to_ir(srcPtr unsafe.Pointer, srcLen int32, namePtr unsafe.Pointer, nameLen int32, triplePtr unsafe.Pointer, tripleLen int32) (n int32) { runtime.InitCShared() src := unsafe.Slice((*byte)(srcPtr), srcLen) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) triple := string(unsafe.Slice((*byte)(triplePtr), tripleLen)) ir := CompileToIR(src, name, triple) idx := int32(len(irResults)) irResults = append(irResults, ir) return idx } //export moxie_compile_ir_len func moxie_compile_ir_len(h int32) (n int32) { runtime.InitCShared() if h < 0 || int32(h) >= len(irResults) { return 0 } return int32(len(irResults[h])) } //export moxie_compile_ir_copy func moxie_compile_ir_copy(h int32, dst unsafe.Pointer, cap int32) (n int32) { runtime.InitCShared() if h < 0 || int32(h) >= len(irResults) { return 0 } ir := irResults[h] n = int32(len(ir)) if n > cap { n = cap } if n > 0 { buf := unsafe.Slice((*byte)(dst), n) copy(buf, ir[:n]) } return n } //export moxie_compile_ir_free func moxie_compile_ir_free(h int32) { runtime.InitCShared() if h >= 0 && int32(h) < len(irResults) { irResults[h] = "" } } //export moxie_register_package func moxie_register_package(pathPtr unsafe.Pointer, pathLen int32, namePtr unsafe.Pointer, nameLen int32) { runtime.InitCShared() EnsureImportRegistry() path := string(unsafe.Slice((*byte)(pathPtr), pathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) ImportRegistry[path] = types.NewTCPackage(path, name) } //export moxie_register_func func moxie_register_func(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, sigPtr unsafe.Pointer, sigLen int32) { runtime.InitCShared() EnsureImportRegistry() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) sigDesc := string(unsafe.Slice((*byte)(sigPtr), sigLen)) pkg := ImportRegistry[pkgPath] if pkg == nil { return } old := ParseTypePkgScope ParseTypePkgScope = pkg.Scope() sig := ParseSignatureDesc(sigDesc) ParseTypePkgScope = old pkg.Scope().Insert(types.NewTCFunc(pkg, name, sig)) } //export moxie_register_var func moxie_register_var(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, typDescPtr unsafe.Pointer, typDescLen int32) { runtime.InitCShared() EnsureImportRegistry() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) typDesc := string(unsafe.Slice((*byte)(typDescPtr), typDescLen)) pkg := ImportRegistry[pkgPath] if pkg == nil { return } typ := ParseTypeDesc(typDesc) pkg.Scope().Insert(types.NewTCVar(pkg, name, typ)) } //export moxie_register_const func moxie_register_const(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, typDescPtr unsafe.Pointer, typDescLen int32, val int64) { runtime.InitCShared() EnsureImportRegistry() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) typDesc := string(unsafe.Slice((*byte)(typDescPtr), typDescLen)) pkg := ImportRegistry[pkgPath] if pkg == nil { return } typ := ParseTypeDesc(typDesc) pkg.Scope().Insert(types.NewTCConst(pkg, name, typ, types.NewConstInt(val))) } //export moxie_register_const_string func moxie_register_const_string(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, valPtr unsafe.Pointer, valLen int32) { runtime.InitCShared() EnsureImportRegistry() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) val := string(unsafe.Slice((*byte)(valPtr), valLen)) pkg := ImportRegistry[pkgPath] if pkg == nil { return } pkg.Scope().Insert(types.NewTCConst(pkg, name, types.Typ[types.UntypedString], types.NewConstStr(val))) } //export moxie_register_iface func moxie_register_iface(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, methodsPtr unsafe.Pointer, methodsLen int32) { runtime.InitCShared() EnsureImportRegistry() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) methodsDesc := string(unsafe.Slice((*byte)(methodsPtr), methodsLen)) pkg := ImportRegistry[pkgPath] if pkg == nil { return } var methods []*types.IfaceMethod if methodsDesc != "" { parts := SplitSemicolon(methodsDesc) for _, p := range parts { eqIdx := -1 for i := 0; i < len(p); i++ { if p[i] == '=' { eqIdx = i break } } if eqIdx < 0 { continue } mname := p[:eqIdx] msig := ParseSignatureDesc(p[eqIdx+1:]) methods = append(methods, types.NewTCIfaceMethod(mname, msig)) } } iface := types.NewTCInterface(methods, nil) iface.Complete() tn := types.NewTypeName(pkg, name, iface) pkg.Scope().Insert(tn) } //export moxie_register_type func moxie_register_type(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, underlyingPtr unsafe.Pointer, underlyingLen int32) { runtime.InitCShared() EnsureImportRegistry() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) name := string(unsafe.Slice((*byte)(namePtr), nameLen)) pkg := ImportRegistry[pkgPath] if pkg == nil { return } var underlying syntax.Type if underlyingLen > 0 { desc := string(unsafe.Slice((*byte)(underlyingPtr), underlyingLen)) underlying = ParseTypeDesc(desc) } else { underlying = types.NewTCStruct(nil, nil) } tn := types.NewTypeName(pkg, name, nil) types.NewNamed(tn, underlying) pkg.Scope().Insert(tn) } //export moxie_register_method func moxie_register_method(pkgPathPtr unsafe.Pointer, pkgPathLen int32, typeNamePtr unsafe.Pointer, typeNameLen int32, methodNamePtr unsafe.Pointer, methodNameLen int32, sigPtr unsafe.Pointer, sigLen int32, ptrRecv int32) { runtime.InitCShared() EnsureImportRegistry() pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen)) typeName := string(unsafe.Slice((*byte)(typeNamePtr), typeNameLen)) methodName := string(unsafe.Slice((*byte)(methodNamePtr), methodNameLen)) sigDesc := string(unsafe.Slice((*byte)(sigPtr), sigLen)) pkg := ImportRegistry[pkgPath] if pkg == nil { return } obj := pkg.Scope().Lookup(typeName) if obj == nil { return } tn, ok := obj.(*types.TypeName) if !ok { return } named, ok := tn.Type().(*types.Named) if !ok { return } old := ParseTypePkgScope ParseTypePkgScope = pkg.Scope() sig := ParseSignatureDesc(sigDesc) ParseTypePkgScope = old var recvType syntax.Type = named if ptrRecv != 0 { recvType = types.NewPointer(named) } sig = types.NewSignature(types.NewTCVar(pkg, "", recvType), sig.Params(), sig.Results(), sig.Variadic()) fn := types.NewTCFunc(pkg, methodName, sig) if ptrRecv != 0 { fn.SetPtrRecv(true) } named.AddMethod(fn) } func SplitSemicolon(s string) (ss []string) { var parts []string start := 0 for i := 0; i < len(s); i++ { if s[i] == ';' { parts = append(parts, s[start:i]) start = i + 1 } } parts = append(parts, s[start:]) return parts } //export moxie_clear_imports func moxie_clear_imports() { runtime.InitCShared() EnsureImportRegistry() ImportRegistry = map[string]*types.TCPackage{} } var ParseTypePkgScope *types.Scope func ParseTypeDesc(desc string) (t syntax.Type) { switch desc { case "bool": return types.Typ[types.Bool] case "int8": return types.Typ[types.Int8] case "int16": return types.Typ[types.Int16] case "int32", "int32": return types.Typ[types.Int32] case "int64": return types.Typ[types.Int64] case "uint8", "byte": return types.Typ[types.Uint8] case "uint16": return types.Typ[types.Uint16] case "uint32", "uint32": return types.Typ[types.Uint32] case "uint64": return types.Typ[types.Uint64] case "float32": return types.Typ[types.Float32] case "float64": return types.Typ[types.Float64] case "string": return types.Typ[types.TCString] case "ptr": return types.Typ[types.UnsafePointer] case "error": errorSig := types.NewSignature(nil, nil, types.NewTuple(types.NewTCVar(nil, "", types.Typ[types.TCString])), false) errorIface := types.NewTCInterface([]*types.IfaceMethod{types.NewTCIfaceMethod("Error", errorSig)}, nil) errorIface.Complete() return errorIface case "struct", "struct{}": return types.NewTCStruct(nil, nil) case "interface{}": emptyIface := types.NewTCInterface(nil, nil) emptyIface.Complete() return emptyIface } if len(desc) > 7 && desc[:7] == "struct{" && desc[len(desc)-1] == '}' { inner := desc[7 : len(desc)-1] if inner == "" { return types.NewTCStruct(nil, nil) } fields := splitCommaBalanced(inner) var vars []*types.TCVar var tags []string for _, f := range fields { ci := -1 for k := 0; k < len(f); k++ { if f[k] == ':' { ci = k break } } if ci < 0 { continue } fname := f[:ci] ftype := ParseTypeDesc(f[ci+1:]) vars = append(vars, types.NewTCVar(nil, fname, ftype)) tags = append(tags, "") } return types.NewTCStruct(vars, tags) } if len(desc) > 2 && desc[0] == '[' && desc[1] == ']' { elem := ParseTypeDesc(desc[2:]) if b, ok := elem.(*types.Basic); ok && b.Kind() == types.Uint8 { return types.Typ[types.TCString] } return types.NewSlice(elem) } if len(desc) > 2 && desc[0] == '[' && desc[1] != ']' { closeBrack := int32(-1) for k := int32(1); k < int32(len(desc)); k++ { if desc[k] == ']' { closeBrack = k break } } if closeBrack > 1 { n := types.ParseIntLocal(desc[1:closeBrack]) elem := ParseTypeDesc(desc[closeBrack+1:]) return types.NewArray(elem, n) } } if len(desc) > 1 && desc[0] == '*' { return types.NewPointer(ParseTypeDesc(desc[1:])) } if len(desc) > 5 && desc[:5] == "chan:" { return types.NewTCChan(types.TCSendRecv, ParseTypeDesc(desc[5:])) } if len(desc) > 7 && desc[:7] == "chan<-:" { return types.NewTCChan(types.TCSendOnly, ParseTypeDesc(desc[7:])) } if len(desc) > 7 && desc[:7] == "<-chan:" { return types.NewTCChan(types.TCRecvOnly, ParseTypeDesc(desc[7:])) } if len(desc) > 4 && desc[:4] == "map[" { depth := int32(0) closeBrack := int32(-1) for k := int32(4); k < int32(len(desc)); k++ { if desc[k] == '[' { depth++ } else if desc[k] == ']' { if depth == 0 { closeBrack = k break } depth-- } } if closeBrack > 4 { key := ParseTypeDesc(desc[4:closeBrack]) val := ParseTypeDesc(desc[closeBrack+1:]) return types.NewTCMap(key, val) } } dotIdx := int32(-1) for i := int32(len(desc)) - 1; i >= 0; i-- { if desc[i] == '.' { dotIdx = i break } } if dotIdx > 0 { pkgPath := desc[:dotIdx] typeName := desc[dotIdx+1:] EnsureImportRegistry() if extPkg, ok := ImportRegistry[pkgPath]; ok { obj := extPkg.Scope().Lookup(typeName) if tn, ok := obj.(*types.TypeName); ok && tn.Type() != nil { return tn.Type() } } } if ParseTypePkgScope != nil { obj := ParseTypePkgScope.Lookup(desc) if tn, ok := obj.(*types.TypeName); ok && tn.Type() != nil { return tn.Type() } } return types.Typ[types.Int32] } func ParseSignatureDesc(desc string) (s *types.Signature) { // format: "param1,param2->result1,result2" or "param1,param2" (void) or "->result" (no params) arrow := -1 for i := 0; i < len(desc)-1; i++ { if desc[i] == '-' && desc[i+1] == '>' { arrow = i break } } var paramStr, resultStr string if arrow >= 0 { paramStr = desc[:arrow] resultStr = desc[arrow+2:] } else { paramStr = desc } var params []*types.TCVar if paramStr != "" { parts := splitComma(paramStr) for _, p := range parts { params = append(params, types.NewTCVar(nil, "", ParseTypeDesc(p))) } } var results []*types.TCVar if resultStr != "" { parts := splitComma(resultStr) for _, r := range parts { results = append(results, types.NewTCVar(nil, "", ParseTypeDesc(r))) } } var pTuple, rTuple *types.Tuple if len(params) > 0 { pTuple = types.NewTuple(params...) } if len(results) > 0 { rTuple = types.NewTuple(results...) } return types.NewSignature(nil, pTuple, rTuple, false) } func splitCommaBalanced(s string) (ss []string) { var parts []string start := 0 depth := 0 for i := 0; i < len(s); i++ { switch s[i] { case '{', '[', '(': depth++ case '}', ']', ')': depth-- case ',': if depth == 0 { parts = append(parts, s[start:i]) start = i + 1 } } } parts = append(parts, s[start:]) return parts } func splitComma(s string) (ss []string) { var parts []string start := 0 for i := 0; i < len(s); i++ { if s[i] == ',' { parts = append(parts, s[start:i]) start = i + 1 } } parts = append(parts, s[start:]) return parts } func ScanGenericDecls(src []byte, name string) { errh := func(err syntax.Error) {} file, _ := syntax.ParseBytes(token.NewFileBase(name|".mx"), src, errh, nil, 0) if file == nil { return } for _, d := range file.DeclList { if fd, ok := d.(*syntax.FuncDecl); ok && fd.Recv == nil && fd.Name.Value != "init" { if len(fd.TParamList) > 0 { if genericFuncDecls == nil { genericFuncDecls = map[string]*syntax.FuncDecl{} } genericFuncDecls[name|"."|fd.Name.Value] = fd } } } }