about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Taskfile.yml7
-rw-r--r--cmd/compmod/compiled.tmpl22
-rw-r--r--cmd/compmod/main.go94
3 files changed, 123 insertions, 0 deletions
diff --git a/Taskfile.yml b/Taskfile.yml
index 56e26f3..a8fc876 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -9,6 +9,7 @@ tasks:
       - go build -o build/bot jinx/cmd/bot
       - go build -o build/vm jinx/cmd/vm
       - go build -o build/lang jinx/cmd/lang
+      - go build -o build/compmod jinx/cmd/compmod
 
   run:
     desc: "Runs Jinx bot binary."
@@ -28,6 +29,12 @@ tasks:
     cmds:
       - build/lang {{.CLI_ARGS}}
 
+  run-compmod:
+    desc: "Runs built-in module compiler."
+    deps: [build]
+    cmds:
+      - build/compmod {{.CLI_ARGS}}
+
   test:
     desc: "Tests all packages."
     cmds:
diff --git a/cmd/compmod/compiled.tmpl b/cmd/compmod/compiled.tmpl
new file mode 100644
index 0000000..2070749
--- /dev/null
+++ b/cmd/compmod/compiled.tmpl
@@ -0,0 +1,22 @@
+package {{.Module}}
+
+import (
+	"jinx/pkg/lang/modules"
+	"jinx/pkg/lang/vm/code"
+)
+
+var moduleCompiled = []byte{
+	{{.Bytes}}
+}
+
+var moduleCode = code.New(code.Raw(moduleCompiled), code.NewDebugInfo("{{.Module}}"))
+
+var Module = modules.NewModule(
+	"",
+	"{{.Module}}",
+	&moduleCode,
+	[]modules.ModuleRef{},
+	[]string{
+		{{range .Globals}}"{{.}}", {{end}}
+	},
+)
diff --git a/cmd/compmod/main.go b/cmd/compmod/main.go
new file mode 100644
index 0000000..7cea8ce
--- /dev/null
+++ b/cmd/compmod/main.go
@@ -0,0 +1,94 @@
+package main
+
+import (
+	_ "embed"
+	"flag"
+	"fmt"
+	"html/template"
+	"jinx/pkg/lang/compiler"
+	"jinx/pkg/lang/parser"
+	"jinx/pkg/lang/scanner"
+	"jinx/pkg/lang/vm/code"
+	"os"
+)
+
+//go:embed compiled.tmpl
+var compiledTemplateString string
+
+type compiledTemplateInfo struct {
+	Module  string
+	Globals []string
+	Bytes   string
+}
+
+func main() {
+	flag.Parse()
+
+	moduleName := flag.Arg(0)
+	if moduleName == "" {
+		exit("no module specified")
+	}
+
+	moduleSourcePath := fmt.Sprintf("pkg/lang/modules/%s/%s.lang", moduleName, moduleName)
+	moduleOutputPath := fmt.Sprintf("pkg/lang/modules/%s/compiled.go", moduleName)
+
+	sourceFile, err := os.Open(moduleSourcePath)
+	if err != nil {
+		exit("could not open file: %v", err)
+	}
+	defer sourceFile.Close()
+
+	scanner := scanner.New(sourceFile)
+	tokens, err := scanner.Scan()
+	if err != nil {
+		exit("error during scanning: %v", err)
+	}
+
+	parser := parser.New(tokens)
+	program, err := parser.Parse()
+	if err != nil {
+		exit("error during parsing: %v", err)
+	}
+
+	comp := compiler.New(moduleName, "", program)
+	module, err := comp.Compile()
+	if err != nil {
+		exit("compilation failed: %v", err)
+	}
+
+	code := turnCodeIntoBytes(module.Code().Code())
+
+	info := compiledTemplateInfo{
+		Module:  module.Name(),
+		Globals: module.Globals(),
+		Bytes:   code,
+	}
+
+	compiledTemplate, err := template.New("compiled").Parse(compiledTemplateString)
+	if err != nil {
+		exit("could not parse template: %v", err)
+	}
+
+	outputFile, err := os.Create(moduleOutputPath)
+	if err != nil {
+		exit("could not create file: %v", err)
+	}
+
+	if err := compiledTemplate.Execute(outputFile, info); err != nil {
+		exit("could not write to file: %v", err)
+	}
+}
+
+func turnCodeIntoBytes(code code.Raw) string {
+	var result string
+	for _, b := range []byte(code) {
+		result += fmt.Sprintf("0x%x, ", b)
+	}
+	return result
+}
+
+func exit(format string, args ...any) {
+	message := fmt.Sprintf(format, args...)
+	fmt.Printf("error: %s\n", message)
+	os.Exit(1)
+}