about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Taskfile.yml7
-rw-r--r--cmd/lang/main.go86
-rw-r--r--cmd/vm/main.go26
3 files changed, 116 insertions, 3 deletions
diff --git a/Taskfile.yml b/Taskfile.yml
index 86f6a8d..56e26f3 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -8,6 +8,7 @@ tasks:
     cmds:
       - go build -o build/bot jinx/cmd/bot
       - go build -o build/vm jinx/cmd/vm
+      - go build -o build/lang jinx/cmd/lang
 
   run:
     desc: "Runs Jinx bot binary."
@@ -21,6 +22,12 @@ tasks:
     cmds:
       - build/vm {{.CLI_ARGS}}
 
+  run-lang:
+    desc: "Runs Lang compiler and VM."
+    deps: [build]
+    cmds:
+      - build/lang {{.CLI_ARGS}}
+
   test:
     desc: "Tests all packages."
     cmds:
diff --git a/cmd/lang/main.go b/cmd/lang/main.go
new file mode 100644
index 0000000..042a84e
--- /dev/null
+++ b/cmd/lang/main.go
@@ -0,0 +1,86 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"jinx/pkg/lang/compiler"
+	"jinx/pkg/lang/parser"
+	"jinx/pkg/lang/scanner"
+	"jinx/pkg/lang/vm"
+	"jinx/pkg/lang/vm/code"
+	"os"
+)
+
+func main() {
+	compile := flag.String("c", "", "compile to file")
+	run := flag.Bool("r", false, "run")
+
+	flag.Parse()
+
+	if *compile == "" && !*run {
+		exit("nothing to do, either -c or -r is required")
+	}
+
+	path := flag.Arg(0)
+	if path == "" {
+		exit("no file specified")
+	}
+
+	file, err := os.Open(path)
+	if err != nil {
+		exit("could not open file: %v", err)
+	}
+	defer file.Close()
+
+	var bc code.Code
+
+	scanner := scanner.New(file)
+	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(program)
+	bc, err = comp.Compile()
+	if err != nil {
+		exit("compilation failed: %v", err)
+	}
+
+	if *compile != "" {
+		output, err := os.Create(*compile)
+		if err != nil {
+			exit("could not create file: %v", err)
+		}
+		defer output.Close()
+
+		if _, err := output.Write(bc.Code()); err != nil {
+			exit("could not write to file: %v", err)
+		}
+	}
+
+	if *run {
+		vm := vm.New(&bc)
+		if err := vm.Run(); err != nil {
+			exit("execution failed: %v", err)
+		}
+
+		res, err := vm.GetResult()
+		if err != nil {
+			exit("could not get result: %v", err)
+		}
+
+		fmt.Println(res)
+	}
+}
+
+func exit(format string, args ...any) {
+	message := fmt.Sprintf(format, args...)
+	fmt.Printf("error: %s\n", message)
+	os.Exit(1)
+}
diff --git a/cmd/vm/main.go b/cmd/vm/main.go
index 19ba160..013899a 100644
--- a/cmd/vm/main.go
+++ b/cmd/vm/main.go
@@ -13,6 +13,7 @@ import (
 func main() {
 	// Take the first argument as the path to the file to be parsed.
 	compile := flag.String("c", "", "compile to file (if not bytecode)")
+	decompile := flag.String("d", "", "decompile bytecode to file")
 	run := flag.Bool("r", false, "run")
 	bytecode := flag.Bool("b", false, "interpret bytecode, without this flag the program will be interpreted in Lang VM text format.")
 
@@ -22,12 +23,16 @@ func main() {
 		exit("-c and -b are mutually exclusive")
 	}
 
+	if *compile != "" && *decompile != "" {
+		exit("-c and -d are mutually exclusive")
+	}
+
 	if *bytecode && !*run {
 		exit("-b requires -r")
 	}
 
-	if *compile == "" && !*run {
-		exit("nothing to do, either -c or -r is required")
+	if *compile == "" && *decompile == "" && !*run {
+		exit("nothing to do, either -c, -d or -r is required")
 	}
 
 	path := flag.Arg(0)
@@ -42,7 +47,7 @@ func main() {
 	defer file.Close()
 
 	var bc code.Code
-	if !*bytecode {
+	if !*bytecode && *decompile == "" {
 		comp := text.NewCompiler(file)
 		bc, err = comp.Compile()
 		if err != nil {
@@ -67,6 +72,21 @@ func main() {
 		}
 
 		bc = code.New(content, code.NewDebugInfo(path))
+
+		if *decompile != "" {
+			decomp := text.NewDecompiler(bc)
+			result := decomp.Decompile()
+
+			output, err := os.Create(*decompile)
+			if err != nil {
+				exit("could not create file: %v", err)
+			}
+			defer output.Close()
+
+			if _, err := output.Write([]byte(result)); err != nil {
+				exit("could not write to file: %v", err)
+			}
+		}
 	}
 
 	if *run {