From 96cf52263053db6bc3069c9fbc664ed0725ac41e Mon Sep 17 00:00:00 2001 From: Kyle Gunger Date: Fri, 19 Nov 2021 01:39:05 -0500 Subject: Some refactoring, clearing out eval + Fixed BuildRoot + Refactored world.go - Deleted most of eval, I'm going to re-do it. --- src/exec.go | 2 +- src/parse.go | 8 ++- src/texec/eval.go | 176 ++++++++++------------------------------------ src/texec/libtnsl.go | 72 +++++++++++-------- src/texec/world.go | 36 ++++++---- src/texec/worldbuilder.go | 66 ++++++++++++----- 6 files changed, 156 insertions(+), 204 deletions(-) diff --git a/src/exec.go b/src/exec.go index b9a74f8..6213e92 100644 --- a/src/exec.go +++ b/src/exec.go @@ -28,5 +28,5 @@ func main() { root := texec.BuildRoot(*inputFile) - texec.EvalTNSL(&root, *progFlags) + //texec.EvalTNSL(&root, *progFlags) } \ No newline at end of file diff --git a/src/parse.go b/src/parse.go index 7849857..79bf013 100644 --- a/src/parse.go +++ b/src/parse.go @@ -18,6 +18,7 @@ package main import "fmt" import "tparse" +import "texec" import "flag" import "os" @@ -35,14 +36,19 @@ func main() { return } - tokens := tparse.TokenizeFile(*inputFile) + switch *writeLevel { case 0: + tokens := tparse.TokenizeFile(*inputFile) fd.WriteString(fmt.Sprint(tokens) + "\n") case 1: + tokens := tparse.TokenizeFile(*inputFile) tree := tparse.MakeTree(&tokens, *inputFile) fd.WriteString(fmt.Sprint(tree) + "\n") + case 2: + root := texec.BuildRoot(*inputFile) + fd.WriteString(fmt.Sprint(root) + "\n") } fd.Close() diff --git a/src/texec/eval.go b/src/texec/eval.go index 9900194..4aefd5a 100644 --- a/src/texec/eval.go +++ b/src/texec/eval.go @@ -16,158 +16,58 @@ package texec -import "strings" -import "strconv" -import "unicode" -import "tparse" -import "fmt" - -// Check if a block is the main function -func funcName(n tparse.Node) string { - if n.Data.Data == "block" { - if n.Sub[0].Data.Data == "bdef" { - for i := 0; i < len(n.Sub[0].Sub); i++ { - if n.Sub[0].Sub[i].Data.Type == tparse.DEFWORD { - return n.Sub[0].Sub[i].Data.Data - } - } - } - } - return "" -} - -// Default values for variables -func defaultVaule(t string) interface{} { - switch t { - case "int", "uint", "int8", "uint8", "char", "charp": - return 0 - case "string": - return "" - } - return nil -} - -// Match specific type (t) with general type (g) -func typeMatches(t, g string) bool { - switch t { - case "int", "uint", "int8", "uint8": - return g == "integer" - case "float": - return g == "float" - case "char", "charp": - return g == "char" - case "string": - return g == "string" - } - return false -} +// Don't want to deal with this rn -// Get the control flow's name -func cfType(n tparse.Node) string { - if n.Data.Data == "block" { - if n.Sub[0].Data.Data == "bdef" { - for i := 0; i < len(n.Sub[0].Sub); i++ { - if n.Sub[0].Sub[i].Data.Type == tparse.KEYWORD { - if n.Sub[0].Sub[i].Data.Data == "if" || n.Sub[0].Sub[i].Data.Data == "elif" || n.Sub[0].Sub[i].Data.Data == "else" || n.Sub[0].Sub[i].Data.Data == "match" || n.Sub[0].Sub[i].Data.Data == "case" || n.Sub[0].Sub[i].Data.Data == "loop" { - return n.Sub[0].Sub[i].Data.Data - } - } - } - } - } +/* + So here's what I care to support at present: + Type checking, basic types, writing to stdout or a file + Variable and state contexts + Reading from files + Raw structs + Appending to arrays + Calling functions and methods + libtnsl stub + + This subset should theoretically be enough to write a compiler. +*/ - return "" -} +//################ +//# Helper Funcs # +//################ -// Get type as string from nodes -func evalType(n tparse.Node) string { - return "" -} - -// Returns generated value and general "type" of value (string, number) -func evalPreLiteral(n tparse.Node) string { - r := tparse.StringAsRunes(n.Data.Data) - l := len(r) - if r[0] == '"' || r[0] == '\'' { - return tparse.RunesAsString(r[1:l - 1]) +func equateType(a, b TType) bool { + if len(a.Pre) != len(b.Pre) || len(a.Post) != len(b.Post) { + return false + } else if len(a.T.Path) != len(b.T.Path) { + return false } - return "" -} -// Returns generated value and general "type" of value (string, number) -func evalLiteral(n tparse.Node) (interface{}, string) { - r := tparse.StringAsRunes(n.Data.Data) - l := len(r) - - if r[0] == '"' { - return tparse.RunesAsString(r[1:l - 1]), "string" - } else if r[0] == '\'' { - return tparse.RunesAsString(r[1:l - 1]), "char" - } else if unicode.IsNumber(r[0]) { - if strings.Contains(n.Data.Data, ".") { - f, _ := strconv.ParseFloat(n.Data.Data, 64) - return f, "float" - } else { - i, _ := strconv.Atoi(n.Data.Data) - return i, "integer" + for i := 0; i < len(a.Pre); i++ { + if a.Pre[i] != b.Pre[i] { + return false } } - return nil, "" -} - -// Evaluates a definition and sets up a TVariable in the context's var map -func evalDef(n tparse.Node, ctx *TContext) { - vars := len(ctx.VarMap) - 1 - - t := evalType(n.Sub[0]) - - for i := 0; i < len(n.Sub[1].Sub); i++ { - name := n.Sub[1].Sub[i].Data.Data - - _, prs := ctx.VarMap[vars][name] - if prs { - panic(fmt.Sprintf("Attempted re-definition of a variable %v", name)) - } - - val := defaultVaule(t) - if n.Sub[1].Sub[i].Data.Data == "=" { - name = n.Sub[1].Sub[i].Sub[0].Data.Data - val = evalValue(n.Sub[1].Sub[i].Sub[1], ctx) + for i := 0; i < len(a.T.Path); i++ { + if a.T.Path[i] != b.T.Path[i] { + return false } - - ctx.VarMap[vars][name] = TVariable{t, val} } -} -func delScopeVars(sV []string, ctx *TContext) { - m := len(ctx.VarMap) - 1 - for i := 0; i < len(sV); i++ { - delete(ctx.VarMap[m], sV[i]) + if a.T.Name != b.T.Name { + return false } -} -// Evaluates a value statement -func evalValue(artifact tparse.Node, ctx *TContext) interface{} { - vars := len(ctx.VarMap) - 1 -} - -// Evaluates a loop -func evalLoop(artifact tparse.Node, ctx *TContext) { - -} - -func evalIf(artifact tparse.Node, ctx *TContext) bool { - var scopeVars []string -} + for i := 0; i < len(a.Post); i++ { + if a.Post[i] != b.Post[i] { + return false + } + } -// Evaluate a block (Assume that all blocks have only one output for now) -func evalBlock(artifact tparse.Node, ctx *TContext) interface{} { - + return true; } +//################# +//# Runtime funcs # +//################# -// EvalTNSL starts the evaluation on the root TModule's main function with the given flags passed to the program -func EvalTNSL(root *TModule, f string) { - flags := strings.Split(f, " ") -} \ No newline at end of file diff --git a/src/texec/libtnsl.go b/src/texec/libtnsl.go index 1d36892..8dedfbf 100644 --- a/src/texec/libtnsl.go +++ b/src/texec/libtnsl.go @@ -30,54 +30,69 @@ import ( - io.File API for file objects */ +// I really hope this works. + +// Generic in-built types +var ( + + tFile = TType{Pre: []string{}, T: TArtifact{Path: []string{"tnsl", "io"}, Name: "File"}, Post: []string{}} + tString = TType{Pre: []string{"{}"}, T: TArtifact{Path: []string{}, Name:"charp"}, Post: []string{}} + tInt = TType{Pre: []string{}, T: TArtifact{Path: []string{}, Name:"int"}, Post: []string{}} + tFloat = TType{Pre: []string{}, T: TArtifact{Path: []string{}, Name:"float"}, Post: []string{}} + tCharp = TType{Pre: []string{}, T: TArtifact{Path: []string{}, Name:"charp"}, Post: []string{}} + tNull = TType{Pre: []string{}, T: TArtifact{Path: []string{}, Name: "null"}, Post: []string{}} +) + // tells if the stub supports a function -func tnslResolve(callPath TPath) bool { - l := len(callPath.Module) - if l < 2 || l > 3 || callPath.Module[0] != "tnsl" || callPath.Module[1] != "io" { - return false +func tnslResolve(callPath TArtifact) int { + l := len(callPath.Path) + if l < 2 || l > 3 || callPath.Path[0] != "tnsl" || callPath.Path[1] != "io" { + return -1 } - if l > 2 && callPath.Module[2] != "File" { - return false + if l > 2 && callPath.Path[2] != "File" { + return -1 } if l > 2 { - if callPath.Artifact == "write" || callPath.Artifact == "read" || callPath.Artifact == "close" { - return true; + if callPath.Name == "write" || callPath.Name == "read" || callPath.Name == "close" { + return 1; } } else { - if callPath.Artifact == "print" || callPath.Artifact == "println" || callPath.Artifact == "open_file" { - return true; + if callPath.Name == "print" || callPath.Name == "println" || callPath.Name == "open_file" { + return 0; } } - return false + return -1 } // evaluate a function call. // in is the variable in (if any) // out is the variable out (if any) // function is the name of the function -func tnslEval(in, out *TVariable, function string) { +func tnslEval(in TVariable, function string) TVariable { switch function { case "print": - tprint(*in) + tprint(in) case "println": - tprintln(*in) + tprintln(in) case "open_file": - topen_file(*in, out) + return topen_file(in) } + return TVariable{tNull, nil} } // evaluate a call on a file object -func tnslFileEval(file, in, out *TVariable, function string) { +func tnslFileEval(file, in TVariable, function string) TVariable { switch function { case "close": tfile_close(file) case "read": - tfile_read(file, out) + return tfile_read(file) case "write": tfile_write(file, in) } + return TVariable{tNull, nil} } // Generic IO funcs @@ -90,45 +105,42 @@ func tprintln(in TVariable) { fmt.Printf("%v\n", in.Data) } -func topen_file(in TVariable, out *TVariable) { - if in.Type != "string" { +func topen_file(in TVariable) TVariable { + if equateType(in.Type, tString) { panic("Tried to open a file, but did not use a string type for the file name.") } fd, err := os.Create(in.Data.(string)) if err != nil { panic(fmt.Sprintf("Failed to open file %v as requested by the program. Aborting.\n%v", in.Data, err)) } - out.Type = "tnsl.io.File" - out.Data = fd + return TVariable{tFile, fd} } // File API // tnsl.io.File.close -func tfile_close(file *TVariable) { - if file.Type == "tnsl.io.File" { +func tfile_close(file TVariable) { + if equateType(file.Type, tFile) { (file.Data).(*os.File).Close() } } // tnsl.io.File.read -func tfile_read(file, out *TVariable) { +func tfile_read(file TVariable) TVariable { b := []byte{1} (file.Data).(*os.File).Read(b) - if out.Data == "uint8" || out.Data == "int8" { - out.Data = b[0] - } + return TVariable{tCharp, rune(b[0])} } // tnsl.io.File.write -func tfile_write(file, in *TVariable) { +func tfile_write(file, in TVariable) { b := []byte{0} - if in.Data == "uint8" || in.Data == "int8" { + if equateType(file.Type, tFile) && (equateType(in.Type, tCharp) || equateType(in.Type, tInt)) { b[0] = (in.Data).(byte) + (file.Data).(*os.File).Write(b) } else { (file.Data).(*os.File).Close() panic(fmt.Sprintf("Failed to write to file, attempted to use unsupported type (%v)\n", in.Type)) } - (file.Data).(*os.File).Write(b) } diff --git a/src/texec/world.go b/src/texec/world.go index 86660bc..83c50fd 100644 --- a/src/texec/world.go +++ b/src/texec/world.go @@ -18,31 +18,37 @@ package texec import "tparse" +// TArtifact represents the path to a specific named object in the node tree. +type TArtifact struct { + Path []string + Name string +} + +// TType represents the type of a variable (including pre and post unary ops) +type TType struct { + Pre []string + T TArtifact + Post []string +} + // TVariable represents a single variable in the program type TVariable struct { - Type string + Type TType Data interface{} } -// TPath represents a pointer to the current module and file -// that the thread is working in. -type TPath struct { - Module []string - Artifact string -} +type VarMap map[string]TVariable -// TContext represents a single thread. +// TContext represents a single call context. type TContext struct { - CallStack []tparse.Node - CallEnv []TPath - VarMap []map[string]TVariable + CallEnv TArtifact + Vars VarMap } // TModule represents a collection of files and sub-modules in a program type TModule struct { - Name string - Files []tparse.Node - Globals []map[string]TVariable - Sub []TModule + Name string + Artifacts []tparse.Node + Sub []TModule } diff --git a/src/texec/worldbuilder.go b/src/texec/worldbuilder.go index f3cd6e6..d85f1ac 100644 --- a/src/texec/worldbuilder.go +++ b/src/texec/worldbuilder.go @@ -18,28 +18,65 @@ package texec import ( "tparse" - "path" ) /** worldbuilder.go - take in a file name and construct a root TModule based on it. */ +// Note: This is good enough, I guess. Gonna mark this as the final version, only update on major errors. + +// Supported features: +// Importing other files +// Sub-modules across files + +// Semi-borked sub-folders: +// Because the builder doesn't preserve the paths you are taking, it will not figure out which folder each file is in properly. +// Technically, you could work around this by making all imports in EVERY FILE EVERYWHERE look as if they are pathed from the folder +// where the root file is, but this would be a headache. I am just planning on fixing this in the full compiler. + +// Returns generated value and general "type" of value (string, number) +func evalPreLiteral(n tparse.Node) string { + r := tparse.StringAsRunes(n.Data.Data) + l := len(r) + if r[0] == '"' || r[0] == '\'' { + return tparse.RunesAsString(r[1:l - 1]) + } + return "" +} + +// Parse a file and make an AST from it. func parseFile(p string) tparse.Node { tokens := tparse.TokenizeFile(p) return tparse.MakeTree(&(tokens), p) } +// Import a file and auto-import sub-modules and files +func importFile(f string, m *TModule) { + froot := parseFile(f) + for n := 0 ; n < len(froot.Sub) ; n++ { + if froot.Sub[n].Data.Data == "block" { + if froot.Sub[n].Sub[0].Sub[0].Data.Data == "module" { + m.Sub = append(m.Sub, buildModule(froot.Sub[n])) + } else { + m.Artifacts = append(m.Artifacts, froot.Sub[n]) + } + } else if froot.Sub[n].Data.Data == "include" { + importFile(evalPreLiteral(froot.Sub[n].Sub[0]), m) + } else { + m.Artifacts = append(m.Artifacts, froot.Sub[n]) + } + } +} + +// Build a module from a module block node func buildModule(module tparse.Node) TModule { out := TModule{} + out.Name = module.Sub[0].Sub[0].Sub[0].Data.Data - for n := 0 ; n < len(module.Sub) ; n++ { - - switch module.Sub[n].Data.Type { - case 11: - - case 10: - + for n := 1 ; n < len(module.Sub) ; n++ { + if module.Sub[n].Data.Data == "include" { + importFile(evalPreLiteral(module.Sub[n].Sub[0]), &out) } } @@ -47,19 +84,10 @@ func buildModule(module tparse.Node) TModule { } // BuildRoot builds the root module, ready for eval -func BuildRoot(file tparse.Node) TModule { +func BuildRoot(file string) TModule { out := TModule{} - out.Files = append(out.Files, file) - - for n := 0 ; n < len(file.Sub) ; n++ { - - switch file.Sub[n].Data.Type { - case 11: - - case 10: - } - } + importFile(file, &out) return out } -- cgit v1.2.3