From 6e0717fa6b42f448cb35ffdd858e75a7bf9eb199 Mon Sep 17 00:00:00 2001 From: wf001 Date: Wed, 7 May 2025 23:49:40 +0900 Subject: [PATCH] show error message with declaring duplicated variable or type --- pkg/parser/parser.go | 2 +- pkg/parser/validator.go | 29 +++++++++++++++++++++++------ script/test-full.sh | 1 + 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index ae0e481..708ad5d 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -448,7 +448,7 @@ func Parse(token *mTypes.Token) *mTypes.Program { log.DebugMessage("code validating") validateReference(prog.Declare.Func, prog.Declare.Func) - validateVarDeclare(prog.Declare.Func, prog.Declare.Func) + validateVarDeclare(prog.Declare.Func, prog.Declare.Func, map[string]bool{}) validateExtendedTypeReference(prog.Declare.Func, prog.Declare.Func) log.DebugMessage("code validated") diff --git a/pkg/parser/validator.go b/pkg/parser/validator.go index 0ee8c93..fbd30c3 100644 --- a/pkg/parser/validator.go +++ b/pkg/parser/validator.go @@ -6,16 +6,29 @@ import ( mTypes "github.com/wf001/modo/pkg/types" ) -func validateVarDeclare(n *mTypes.Node, root *mTypes.Node) { +func validateVarDeclare(n *mTypes.Node, root *mTypes.Node, used map[string]bool) { + // To check for duplicate variable and type declarations, a Set would be appropriate, + // but since Go doesn't include one in the standard library, + // implement provisionally using a map to avoid extra dependencies. if n == nil { return } if n.Kind == mTypes.ND_TYPE_DECLARE { + used[n.Val] = true return } if n.Kind == mTypes.ND_VAR_DECLARE { + if used[n.Val] { + log.Panic( + "%s: cannot use %s, already used", + error.ERROR_SYNTAX_ERROR, + n.Val, + ) + } + used[n.Val] = true + if n.Child == nil { log.Panic( "%s: the value of '%s' not defined", @@ -25,11 +38,9 @@ func validateVarDeclare(n *mTypes.Node, root *mTypes.Node) { } else if n.Child.Type == nil { // skip validating - validateVarDeclare(n.Child, root) } else if n.Child.Kind == mTypes.ND_FUNCCALL { // skip validating - validateVarDeclare(n.Child, root) } else if n.Type.Value != n.Child.Type.Value { log.Panic( @@ -41,9 +52,15 @@ func validateVarDeclare(n *mTypes.Node, root *mTypes.Node) { ) } } - validateVarDeclare(n.Next, root) - validateVarDeclare(n.Child, root) - validateVarDeclare(n.Bind, root) + if n.Next != nil { + validateVarDeclare(n.Next, root, used) + } + if n.Child != nil { + validateVarDeclare(n.Child, root, used) + } + if n.Bind != nil { + validateVarDeclare(n.Bind, root, used) + } } func findDeclare(n *mTypes.Node, targetVal string) *mTypes.Node { diff --git a/script/test-full.sh b/script/test-full.sh index 91ccef2..b076b0f 100755 --- a/script/test-full.sh +++ b/script/test-full.sh @@ -357,6 +357,7 @@ testexec(){ assertexec '(def main ::int (fn [] (let [x ::int "hello"] (prn x))))' "level=error msg=\"syntax error: cannot use string type as x (int type)\"" assertexec '(def main ::int (fn [] (let [x ::bool "true"] (prn x))))' "level=error msg=\"syntax error: cannot use string type as x (bool type)\"" assertexec '(defschema Person {:name :: string :age :: int :isMale :: bool})(def main :: int (fn [] (let [node :: node {:age 20 :name "richard" :isMale true}] (prn (get node :name)) (prn (get node :age)) (prn (get node :isMale))))))' "level=error msg=\"syntax error: undefined type: node\"" + assertexec '(def main ::int (fn [] (let [x ::int 1 x ::int 2] (prn x))))' "level=error msg=\"syntax error: cannot use x, already used\"" } build-compiler