diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7b6e4ee..564bff0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.22 + go-version: 1.23 - name: Set up gotestfmt uses: gotesttools/gotestfmt-action@v2 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7fbb802..6edbb4d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -17,12 +17,16 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: ">=1.22.2" + go-version: ">=1.23.0" - - run: go mod tidy + - name: Go tidy + run: go mod tidy # - name: Run tests # run: go test ./... + - name: Zip examples directory + run: zip -r examples.zip examples/ + - name: Run GoReleaser uses: goreleaser/goreleaser-action@v3 with: @@ -31,3 +35,11 @@ jobs: args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload examples zip to release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: examples.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 4304748..ea40c57 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -9,17 +9,7 @@ before: builds: - id: build_archive_all env: [CGO_ENABLED=0] - main: ./src - goos: - - linux - - windows - - darwin - goarch: - - amd64 - - arm64 - - id: build_no_archive - env: [CGO_ENABLED=0] - main: ./src + main: ./src/cmd/duwa goos: - linux - windows @@ -27,11 +17,21 @@ builds: goarch: - amd64 - arm64 + # - id: build_no_archive + # env: [CGO_ENABLED=0] + # main: ./src/cmd/duwa + # goos: + # - linux + # - windows + # - darwin + # goarch: + # - amd64 + # - arm64 archives: - id: archive_noncgo builds: [build_archive_all] format: zip - - id: binary_noncgo - builds: [build_no_archive] - format: binary + # - id: binary_noncgo + # builds: [build_no_archive] + # format: binary diff --git a/.vscode/launch.json b/.vscode/launch.json index 8a865c8..e82b881 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,12 +9,25 @@ "type": "go", "request": "launch", "mode": "auto", - "program": "${workspaceRoot}/src/main.go", + "program": "${workspaceRoot}\\src\\cmd\\duwa\\duwa.go", + "console": "integratedTerminal", + "cwd": "${workspaceRoot}", + "args": [ + "-f", + "${workspaceRoot}\\examples\\games\\tictactoe.duwa" + ] + }, + { + "name": "Wasm", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceRoot}/src/cmd/wasm/duwa.go", "console": "integratedTerminal", "cwd": "${workspaceFolder}", "args": [ "-f", - "./examples/games/tictactoe.ny" + "${workspaceRoot}\\examples\\games\\tictactoe.duwa" ] } ] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..af8cf66 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +# Start with the official TinyGo development image +FROM tinygo/tinygo-dev:latest + +# Install additional development tools +RUN apt-get update && apt-get install -y \ + git \ + make \ + gcc \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# Install Go tools +RUN go install golang.org/x/tools/cmd/goimports@latest \ + && go install github.com/go-delve/delve/cmd/dlv@latest + +# Set working directory +WORKDIR /app + +# Add convenient aliases and environment variables +RUN echo 'alias tg="tinygo"' >> ~/.bashrc \ + && echo 'alias tgb="tinygo build"' >> ~/.bashrc \ + && echo 'alias tgr="tinygo run"' >> ~/.bashrc + +# Set default command +CMD ["/bin/bash"] \ No newline at end of file diff --git a/Makefile b/Makefile index 1a2ec6f..3a22aa6 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,22 @@ build: - @go build -C src -o ../bin/duwa + @go build -C ./src/cmd/duwa -o ../../../bin/duwa + +build-wasm: + set TEMP=U:\projects\skybox\duwa\chewa\temp + @GOOS=js GOARCH=wasm tinygo build -o ../duwa-site/public/duwa.wasm -opt 1 ./src/cmd/wasm/duwa.go + +build-docker: + @docker-compose up -d + @docker-compose exec tinygo-dev tinygo build -o ./bin/duwa.wasm -target=wasm ./src/cmd/wasm/duwa.go + @docker-compose exec tinygo-dev cp /tinygo/targets/wasm_exec.js /app/bin/ + @cp ./bin/duwa.wasm ../duwa-site/public/duwa.wasm + @cp ./bin/wasm_exec.js ../duwa-site/public/wasm_exec.js + @docker-compose stop + +build-all: build build-wasm dev: - @go run src/main.go + @go run ./src/cmd/duwa/duwa.go run: build @./bin/duwa diff --git a/README.md b/README.md index 21293e3..962963f 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,58 @@ # Duwa Programming Language (in progress) -This project is an interpreter for the `duwa` language (Name still getting worked shopped), a language based on the Chewa Bantu language. -This project is written in Golang. - -## Setup and Moni ku dziko -Download the prebuilt binaries for your platform from the [release](https://github.com/sevenreup/duwa/releases) - -Create a new source file, `main.ny` (💀 for now the extension does not matter). -Paste the following cool code - -```c# -lemba("Moni Dziko"); -``` -Run you new application +This project is an interpreter for the `duwa` language, a language based on the Chewa Bantu language. +This project is written in Golang. -```bash -duwa -f ./main.ny -``` +## Setup and Zowerenga -## Zowerenga bwa? -Documentation is on its way, stay on the cutting edge and freestyle your code. +Follow the setup documentations at [official site](https://www.duwa.cphiri.dev) You can check out examples in `./examples` folder to quickly see what the language can do for now. ## Main Milestones + - [ ] Create an initial interpreter - - [x] Lexer - - [x] Parser (Recursive descent parser) - - [x] Interpreter (Tree walking interpreter) + - [x] Lexer + - [x] Parser (Recursive descent parser) + - [x] Interpreter (Tree walking interpreter) - [ ] Language features - - [x] Basic data types (string, numbers) - - [x] arithmetic operations - - [x] Control Flow (loops, conditional statements) - - [x] Functions - - [ ] Type checking (for now it does not strictly enforce types) - - [ ] Data Structures - - [x] arrays - - [x] dictionaries - - [ ] Input/ Output - - [ ] Error Handling - - [ ] Class support + - [x] Basic data types (string, numbers) + - [x] arithmetic operations + - [x] Control Flow (loops, conditional statements) + - [x] Functions + - [ ] Type checking (for now it does not strictly enforce types) + - [ ] Data Structures + - [x] arrays + - [x] dictionaries + - [ ] Input/ Output + - [ ] Error Handling + - [ ] Class support + - [ ] Working Wasm version of the language Other Milestones + - [ ] Modularity - [ ] Standard library + +## Building + +### Prerequisites + +- TinyGo: If you are building the web assembly version you will need `TinyGo`, hers is the [setup](https://tinygo.org/getting-started/install) +- GNU make (optional): Using make for building. You can build the project without `make`. + +### Builing/running + +You can build the project by running any of the build make tasks + +```bash +make build +``` + +Or you can run go build directly + +```bash +go build -C ./src/cmd/duwa -o ../../../bin/duwa +``` + +Running the project is the same either use make or go directly diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..604d9b8 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,23 @@ +version: "3.8" + +services: + tinygo-dev: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/app + - go-cache:/home/dev/go + - go-mod-cache:/go/pkg/mod + environment: + - GOPATH=/go + - GOCACHE=/home/dev/go + - GOMODCACHE=/go/pkg/mod + ports: + - "2345:2345" # for delve debugger + tty: true + stdin_open: true + +volumes: + go-cache: + go-mod-cache: diff --git a/go.mod b/go.mod index 87eafcd..eb79f93 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/sevenreup/duwa -go 1.22.0 +go 1.23.0 require github.com/shopspring/decimal v1.3.1 diff --git a/src/main.go b/src/cmd/duwa/duwa.go similarity index 50% rename from src/main.go rename to src/cmd/duwa/duwa.go index d49cfcd..84de335 100644 --- a/src/main.go +++ b/src/cmd/duwa/duwa.go @@ -3,8 +3,12 @@ package main import ( "flag" "log" + "log/slog" + "os" "github.com/sevenreup/duwa/src/duwa" + "github.com/sevenreup/duwa/src/object" + "github.com/sevenreup/duwa/src/runtime/native" ) var ( @@ -22,6 +26,8 @@ func main() { log.Fatal("Please provide a file to run") } - duwa := duwa.New(file) - duwa.Run() + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + console := native.NewConsole() + duwa := duwa.New(object.New(logger, console)) + duwa.RunFile(file) } diff --git a/src/cmd/wasm/duwa.go b/src/cmd/wasm/duwa.go new file mode 100644 index 0000000..b882a05 --- /dev/null +++ b/src/cmd/wasm/duwa.go @@ -0,0 +1,99 @@ +//go:build js && wasm + +package main + +import ( + "context" + "fmt" + "log/slog" + "runtime" + "syscall/js" + + "github.com/sevenreup/duwa/src/duwa" + "github.com/sevenreup/duwa/src/object" + "github.com/sevenreup/duwa/src/runtime/wasm" +) + +var compiler *duwa.Duwa + +type WasmConsoleHandler struct { +} + +func main() { + // Ensure the garbage collector runs more frequently + runtime.SetFinalizer(compiler, nil) + logger := slog.New(&WasmConsoleHandler{}) + console := wasm.NewConsole() + compiler = duwa.New(object.New(logger, console)) + + // Register the function in the global scope + js.Global().Set("duwaRun", js.FuncOf(wasm.WrapFunction(run))) + + fmt.Println("Duwa is ready") + + // Keep the program running + <-make(chan bool) +} + +func run(this js.Value, inputs []js.Value) interface{} { + if len(inputs) < 1 { + return wasm.Wrap("Please provide a file to run") + } + + file := inputs[0].String() + result := compiler.Run(file) + if result == nil { + return wasm.Wrap(nil) + } + return wasm.Wrap(result.Inspect()) +} + +func (h *WasmConsoleHandler) Enabled(_ context.Context, level slog.Level) bool { + return true +} + +func emitConsoleLogEvent(r slog.Record) { + eventInit := js.Global().Get("Object").New() + eventInit.Set("message", r.Message) + eventInit.Set("level", slogLevelToConsoleLevel(r.Level)) + logType := "runtime" + for attr := range r.Attrs { + if attr.Key == "type" { + logType = attr.Value.String() + } + } + eventInit.Set("type", logType) + wasm.DispatchEvent("duwaLogEvent", eventInit) +} + +func (h *WasmConsoleHandler) Handle(ctx context.Context, r slog.Record) error { + emitConsoleLogEvent(r) + return nil +} + +func (h *WasmConsoleHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return h +} + +func (h *WasmConsoleHandler) WithGroup(name string) slog.Handler { + return h +} + +func (h *WasmConsoleHandler) Handler() slog.Handler { + return h +} + +func slogLevelToConsoleLevel(level slog.Level) string { + switch level { + case slog.LevelDebug: + return "debug" + case slog.LevelInfo: + return "info" + case slog.LevelWarn: + return "warn" + case slog.LevelError: + return "error" + default: + return "info" + } +} diff --git a/src/duwa/duwa.go b/src/duwa/duwa.go index 3b4e109..1a327b4 100644 --- a/src/duwa/duwa.go +++ b/src/duwa/duwa.go @@ -13,32 +13,37 @@ import ( ) type Duwa struct { - file string Environment *object.Environment } -func New(file string) *Duwa { +func New(env *object.Environment) *Duwa { duwa := &Duwa{ - file: file, - Environment: object.NewEnvironment(), + Environment: env, } duwa.registerEvaluator() return duwa } -func (c *Duwa) Run() { - file, err := os.ReadFile(c.file) +func (c *Duwa) RunFile(filePath string) object.Object { + file, err := os.ReadFile(filePath) if err != nil { log.Fatal(err) } - l := lexer.New(file) + return c.run(file) +} + +func (c *Duwa) Run(data string) object.Object { + return c.run([]byte(data)) +} + +func (c *Duwa) run(data []byte) object.Object { + l := lexer.New(data) p := parser.New(l) - env := object.NewEnvironment() program := p.ParseProgram() if len(p.Errors()) != 0 { - utils.PrintParserErrors(os.Stdout, p.Errors()) + utils.PrintParserErrors(c.Environment.Logger, p.Errors()) } - evaluator.Eval(program, env) + return evaluator.Eval(program, c.Environment) } func (c *Duwa) registerEvaluator() { diff --git a/src/evaluator/evaluator_test.go b/src/evaluator/evaluator_test.go index fd59805..bfcd35c 100644 --- a/src/evaluator/evaluator_test.go +++ b/src/evaluator/evaluator_test.go @@ -1,19 +1,20 @@ package evaluator import ( + "testing" + "github.com/sevenreup/duwa/src/lexer" "github.com/sevenreup/duwa/src/object" "github.com/sevenreup/duwa/src/parser" "github.com/sevenreup/duwa/src/values" "github.com/shopspring/decimal" - "testing" ) func testEval(input string) object.Object { l := lexer.New([]byte(input)) p := parser.New(l) program := p.ParseProgram() - env := object.NewEnvironment() + env := object.Default() evaluatorInstance := Eval object.RegisterEvaluator(evaluatorInstance) diff --git a/src/library/functions/print.go b/src/library/functions/print.go index edc405f..9d470b6 100644 --- a/src/library/functions/print.go +++ b/src/library/functions/print.go @@ -1,10 +1,8 @@ package functions import ( - "fmt" "github.com/sevenreup/duwa/src/object" "github.com/sevenreup/duwa/src/token" - "os" "strings" ) @@ -15,8 +13,7 @@ func Print(env *object.Environment, tok token.Token, args ...object.Object) obje for _, value := range args { str = append(str, value.Inspect()) } - - fmt.Print(strings.Join(str, " ")) + env.Logger.Info(strings.Join(str, " ")) } return nil @@ -30,9 +27,9 @@ func PrintLine(env *object.Environment, tok token.Token, args ...object.Object) str = append(str, value.Inspect()) } - fmt.Fprintln(os.Stdout, strings.Join(str, " ")) + env.Logger.Info(strings.Join(str, " ") + "\n") } else { - fmt.Fprintln(os.Stdout) + env.Logger.Info("\n") } return nil diff --git a/src/library/library.go b/src/library/library.go index ae390b4..8ce88e0 100644 --- a/src/library/library.go +++ b/src/library/library.go @@ -13,7 +13,7 @@ func init() { RegisterModule("console", modules.ConsoleMethods) RegisterModule("math", modules.MathMethods) - RegisterFunction("lemba", functions.Print) + RegisterFunction("lemba", functions.PrintLine) RegisterFunction("lembanzr", functions.PrintLine) RegisterFunction("kuNambala", functions.ParseStringToNumber) } diff --git a/src/library/modules/console.go b/src/library/modules/console.go index c5f2ee4..9b5775e 100644 --- a/src/library/modules/console.go +++ b/src/library/modules/console.go @@ -1,11 +1,7 @@ package modules import ( - "bufio" "fmt" - "os" - "os/exec" - "runtime" "strconv" "strings" @@ -29,49 +25,42 @@ func consolePrint(env *object.Environment, tok token.Token, args ...object.Objec values = append(values, value.Inspect()) } - libPrint(values) + libPrint(env, values) return nil } func consoleRead(scope *object.Environment, tok token.Token, args ...object.Object) object.Object { - scanner := bufio.NewScanner(os.Stdin) - if len(args) == 1 { prompt := args[0].(*object.String).Value fmt.Print(prompt) } - val := scanner.Scan() + val, err := scope.Console.Read() - if !val { + if err != nil { return values.NULL } - return &object.String{Value: scanner.Text()} + return &object.String{Value: val} } func consoleClear(scope *object.Environment, tok token.Token, args ...object.Object) object.Object { - if runtime.GOOS == "windows" { - cmd := exec.Command("cmd", "/c", "cls") - cmd.Stdout = os.Stdout - cmd.Run() - } else { - cmd := exec.Command("clear") - cmd.Stdout = os.Stdout - cmd.Run() + err := scope.Console.Clear() + if err != nil { + return values.NULL } - return nil + return values.NULL } -func libPrint(values []string) { +func libPrint(env *object.Environment, values []string) { if len(values) > 0 { str := make([]string, 0) str = append(str, values...) strRaw, _ := strconv.Unquote(`"` + strings.Join(str, " ") + `"`) - fmt.Print(strRaw) + env.Logger.Info(strRaw) } } diff --git a/src/library/runtime/console.go b/src/library/runtime/console.go new file mode 100644 index 0000000..5e9f1ea --- /dev/null +++ b/src/library/runtime/console.go @@ -0,0 +1,6 @@ +package runtime + +type Console interface { + Read() (string, error) + Clear() error +} diff --git a/src/object/environment.go b/src/object/environment.go index ec6b72d..b0f16ac 100644 --- a/src/object/environment.go +++ b/src/object/environment.go @@ -1,18 +1,35 @@ package object +import ( + "log/slog" + + "github.com/sevenreup/duwa/src/library/runtime" + "github.com/sevenreup/duwa/src/runtime/native" +) + func NewEnclosedEnvironment(outer *Environment) *Environment { - env := NewEnvironment() + env := Default() env.outer = outer return env } -func NewEnvironment() *Environment { + +func Default() *Environment { + logger := slog.Default() + s := make(map[string]Object) + console := native.NewConsole() + return &Environment{store: s, outer: nil, Logger: logger, Console: console} +} + +func New(logger *slog.Logger, console runtime.Console) *Environment { s := make(map[string]Object) - return &Environment{store: s, outer: nil} + return &Environment{store: s, outer: nil, Logger: logger, Console: console} } type Environment struct { - store map[string]Object - outer *Environment + store map[string]Object + outer *Environment + Logger *slog.Logger + Console runtime.Console } func (e *Environment) Get(name string) (Object, bool) { diff --git a/src/repl/repl.go b/src/repl/repl.go index 5d7a5ce..0f86281 100644 --- a/src/repl/repl.go +++ b/src/repl/repl.go @@ -3,11 +3,13 @@ package repl import ( "bufio" "fmt" + "io" + "log/slog" + "github.com/sevenreup/duwa/src/evaluator" "github.com/sevenreup/duwa/src/object" "github.com/sevenreup/duwa/src/parser" "github.com/sevenreup/duwa/src/utils" - "io" "github.com/sevenreup/duwa/src/lexer" ) @@ -17,9 +19,10 @@ const PROMPT = ">> " func Start(in io.Reader, out io.Writer) { object.RegisterEvaluator(evaluator.Eval) scanner := bufio.NewScanner(in) - env := object.NewEnvironment() + env := object.Default() + log := slog.Default() for { - fmt.Printf(PROMPT) + fmt.Print(PROMPT) scanned := scanner.Scan() if !scanned { return @@ -29,7 +32,7 @@ func Start(in io.Reader, out io.Writer) { p := parser.New(l) program := p.ParseProgram() if len(p.Errors()) != 0 { - utils.PrintParserErrors(out, p.Errors()) + utils.PrintParserErrors(log, p.Errors()) continue } evaluated := evaluator.Eval(program, env) diff --git a/src/runtime/native/console.go b/src/runtime/native/console.go new file mode 100644 index 0000000..2ea0dd6 --- /dev/null +++ b/src/runtime/native/console.go @@ -0,0 +1,40 @@ +package native + +import ( + "bufio" + "errors" + "os" + "os/exec" + "runtime" +) + +type NativeConsole struct { +} + +func NewConsole() *NativeConsole { + return &NativeConsole{} +} + +func (nc *NativeConsole) Read() (string, error) { + scanner := bufio.NewScanner(os.Stdin) + val := scanner.Scan() + + if !val { + return "", errors.New("failed to read from console") + } + + return scanner.Text(), nil +} + +func (nc *NativeConsole) Clear() error { + if runtime.GOOS == "windows" { + cmd := exec.Command("cmd", "/c", "cls") + cmd.Stdout = os.Stdout + cmd.Run() + } else { + cmd := exec.Command("clear") + cmd.Stdout = os.Stdout + cmd.Run() + } + return nil +} diff --git a/src/runtime/wasm/console.go b/src/runtime/wasm/console.go new file mode 100644 index 0000000..9e0a47a --- /dev/null +++ b/src/runtime/wasm/console.go @@ -0,0 +1,62 @@ +//go:build js && wasm + +package wasm + +import ( + "errors" + "fmt" + "syscall/js" +) + +type WasmConsole struct { + inputChan chan string + errorChan chan error + ready chan struct{} +} + +func NewConsole() *WasmConsole { + console := &WasmConsole{ + inputChan: make(chan string), + errorChan: make(chan error), + ready: make(chan struct{}), + } + + js.Global().Set("duwaConsoleProcessInput", js.FuncOf(WrapFunction(console.processInput))) + js.Global().Set("duwaConsoleReady", js.FuncOf(WrapFunction(console.consoleReady))) + + <-console.ready + + return console +} + +func (wc *WasmConsole) Read() (string, error) { + select { + case input := <-wc.inputChan: + return input, nil + case err := <-wc.errorChan: + return "", err + } +} + +func (wc *WasmConsole) Clear() error { + eventInit := js.Global().Get("Object").New() + eventInit.Set("command", "clear") + DispatchEvent("duwaConsoleCommandEvent", eventInit) + return nil +} + +func (wc *WasmConsole) processInput(this js.Value, args []js.Value) interface{} { + if len(args) > 0 { + fmt.Println("Received input:", args[0].String()) + wc.inputChan <- args[0].String() + } else { + wc.errorChan <- errors.New("no input received") + } + return nil +} + +func (wc *WasmConsole) consoleReady(this js.Value, args []js.Value) interface{} { + close(wc.ready) + fmt.Println("Console emulator is ready") + return nil +} diff --git a/src/runtime/wasm/helpers.go b/src/runtime/wasm/helpers.go new file mode 100644 index 0000000..2d76df6 --- /dev/null +++ b/src/runtime/wasm/helpers.go @@ -0,0 +1,61 @@ +//go:build js && wasm + +package wasm + +import ( + "fmt" + "syscall/js" +) + +// wrapFunction provides error handling for JavaScript functions +func WrapFunction(fn func(this js.Value, args []js.Value) interface{}) func(this js.Value, args []js.Value) interface{} { + return func(this js.Value, args []js.Value) interface{} { + defer func() { + if r := recover(); r != nil { + // Convert panic to JavaScript error object + errorObj := make(map[string]interface{}) + errorObj["error"] = fmt.Sprint(r) + Wrap(errorObj) + } + }() + return fn(this, args) + } +} + +// wrap safely converts Go values to JavaScript values +func Wrap(value interface{}) interface{} { + switch val := value.(type) { + case nil: + return js.Null() + case string: + return js.ValueOf(val) + case int, int32, int64, float32, float64: + return js.ValueOf(val) + case bool: + return js.ValueOf(val) + case []interface{}: + arr := make([]interface{}, len(val)) + for i, v := range val { + arr[i] = Wrap(v) + } + return js.ValueOf(arr) + case map[string]interface{}: + obj := js.Global().Get("Object").New() + for k, v := range val { + obj.Set(k, Wrap(v)) + } + return obj + default: + return js.ValueOf(fmt.Sprint(val)) + } +} + +func DispatchEvent(name string, detail js.Value) { + eventData := js.Global().Get("Object").New() + eventData.Set("detail", map[string]interface{}{ + "detail": detail, + "type": name, + }) + event := js.Global().Get("CustomEvent").New("goConsoleEvent", eventData) + js.Global().Get("window").Call("dispatchEvent", event) +} diff --git a/src/utils/logging.go b/src/utils/logging.go index 48dcc20..c625500 100644 --- a/src/utils/logging.go +++ b/src/utils/logging.go @@ -1,14 +1,19 @@ package utils -import "io" +import ( + "log/slog" + "strings" +) const ERROR_HEDEAR = "Errorr!!" -func PrintParserErrors(out io.Writer, errors []string) { - io.WriteString(out, ERROR_HEDEAR) - io.WriteString(out, "Woops! We ran into some monkey business here!\n") - io.WriteString(out, " parser errors:\n") +func PrintParserErrors(logger *slog.Logger, errors []string) { + var builder strings.Builder + builder.WriteString(ERROR_HEDEAR) + builder.WriteString("Parser errors:\n") for _, msg := range errors { - io.WriteString(out, "\t"+msg+"\n") + builder.WriteString("\t" + msg + "\n") } + + logger.Error(builder.String(), "type", "parser") }