anglais/wasm/wasm.go
Neemek 7879184d56
All checks were successful
/ test (push) Successful in 51s
fix wasm and tests
2026-07-15 21:46:14 +02:00

156 lines
3.2 KiB
Go

//go:build wasm && go1.24
package main
import (
"errors"
"log"
"syscall/js"
"neemek.com/anglais/core"
)
type JsResolver struct {
jsResolver js.Value
}
func (r *JsResolver) IsSame(a, b string) bool {
return a == b
}
func (r *JsResolver) Resolve(_, name string) (*core.ImportResult, error) {
jsv := r.jsResolver.Invoke(name)
if jsv.Type() == js.TypeUndefined {
return nil, errors.New("cannot find import with name " + name)
}
if jsv.Type() != js.TypeString {
return nil, errors.New("invalid value for source: " + jsv.String())
}
return &core.ImportResult{
Source: jsv.String(),
Path: name,
}, nil
}
func jsError(err error) interface{} {
return jsErrorOfString(err.Error())
}
func jsErrorOfString(err string) interface{} {
errorConstructor := js.Global().Get("Error")
errorObject := errorConstructor.New(err)
return errorObject
}
func run(_ js.Value, args []js.Value) interface{} {
source := args[0].String()
outputHandler := args[1]
errorHandler := args[2]
resolver := args[3]
log.Printf("got source: %s", source)
lexer := core.NewLexer(source)
tokens, err := lexer.Tokenize()
if err != nil {
return jsError(err)
}
log.Printf("got tokens: %v", tokens)
parser := core.NewParser(source, []string{}, tokens)
tree, err := parser.Parse(source)
if err != nil {
var e core.FormatedError
if errors.As(err, &e) {
errorHandler.Invoke(e.Format())
return nil
}
errorHandler.Invoke(err.Error())
return nil
}
log.Printf("Parsed tree: %s", tree.String())
compiler := core.NewCompiler([]rune(source))
log.Println("Set imports resolver")
compiler.SetImportsResolver(&JsResolver{
resolver,
})
defer func() {
if err := recover(); err != nil {
log.Printf("panic recovered: %v", err)
}
}()
_, err = compiler.Compile(tree)
if err != nil {
var e core.CompilerError
if errors.As(err, &e) {
errorHandler.Invoke(e.Format())
return nil
}
errorHandler.Invoke(err.Error())
return nil
}
log.Printf("Compiled tree (into %v instructions)", len(compiler.Chunk.Bytecode))
vm := core.NewVM(compiler.Chunk, 256, 256)
// overwrite output
vm.SetGlobal("write", &core.BuiltinFunctionValue{
Name: "write",
Signature: &core.FunctionSignature{
In: []core.TypeSignature{&core.StringSignature{}},
Out: &core.NilSignature{},
},
F: func(vm *core.VM, this core.Value, args []core.Value) (core.Value, error) {
s := args[0].String()
log.Printf("Writing value: %s", s)
outputHandler.Invoke(js.ValueOf(s + "\n"))
return nil, nil
},
})
vm.SetGlobal("print", &core.BuiltinFunctionValue{
Name: "print",
Signature: &core.FunctionSignature{
In: []core.TypeSignature{&core.StringSignature{}},
Out: &core.NilSignature{},
},
F: func(vm *core.VM, this core.Value, args []core.Value) (core.Value, error) {
s := args[0].String()
log.Printf("Printing value: %s", s)
outputHandler.Invoke(js.ValueOf(s))
return nil, nil
},
})
for vm.Next() {
}
log.Println("Finished executing")
return js.Null()
}
func main() {
log.Println("Initializing Anglais WASM module")
js.Global().Set("run", js.FuncOf(run))
log.Println("Initialized Anglais WASM module")
// keep alive so run func can be used
select {}
}