add CST validation for unique variable names in two-variable and multivariable for-loops

This commit is contained in:
John Kerl 2021-01-21 00:48:52 -05:00
parent a878fff553
commit 8a7d24ede0
4 changed files with 95 additions and 14 deletions

View file

@ -273,7 +273,6 @@ RAW AST:
* full record "$*"
* statement block
Exit status was 0; expected 1.
mlr -n put -v for (k, k in @*) {}
DSL EXPRESSION:
@ -286,7 +285,6 @@ RAW AST:
* full oosvar "@*"
* statement block
Exit status was 0; expected 1.
mlr -n put -v for ((a,a), c in @*) {}
DSL EXPRESSION:
@ -301,7 +299,6 @@ RAW AST:
* full oosvar "@*"
* statement block
Exit status was 0; expected 1.
mlr -n put -v for ((a,b), a in @*) {}
DSL EXPRESSION:
@ -316,7 +313,6 @@ RAW AST:
* full oosvar "@*"
* statement block
Exit status was 0; expected 1.
mlr -n put -v for ((a,b), b in @*) {}
DSL EXPRESSION:
@ -331,7 +327,6 @@ RAW AST:
* full oosvar "@*"
* statement block
Exit status was 0; expected 1.
mlr -n put -v for ((a,a,c), d in @*) {}
DSL EXPRESSION:
@ -347,7 +342,6 @@ RAW AST:
* full oosvar "@*"
* statement block
Exit status was 0; expected 1.
mlr -n put -v for ((a,b,a), d in @*) {}
DSL EXPRESSION:
@ -363,7 +357,6 @@ RAW AST:
* full oosvar "@*"
* statement block
Exit status was 0; expected 1.
mlr -n put -v for ((a,b,c), a in @*) {}
DSL EXPRESSION:
@ -379,7 +372,6 @@ RAW AST:
* full oosvar "@*"
* statement block
Exit status was 0; expected 1.
mlr -n put -v for ((a,b,b), d in @*) {}
DSL EXPRESSION:
@ -395,7 +387,6 @@ RAW AST:
* full oosvar "@*"
* statement block
Exit status was 0; expected 1.
mlr -n put -v for ((a,b,c), b in @*) {}
DSL EXPRESSION:
@ -411,7 +402,6 @@ RAW AST:
* full oosvar "@*"
* statement block
Exit status was 0; expected 1.
mlr -n put -v for ((a,b,c), c in @*) {}
DSL EXPRESSION:
@ -427,7 +417,6 @@ RAW AST:
* full oosvar "@*"
* statement block
Exit status was 0; expected 1.
mlr --from ./reg-test/input/xyz2 put -q func f() { return {"a"."b":"c"."d",3:4}}; for (k,v in f()){print "k=".k.",v=".v}
k=ab,v=cd

View file

@ -9,8 +9,10 @@ package cst
import (
"errors"
"fmt"
"os"
"miller/dsl"
"miller/lib"
)
// ----------------------------------------------------------------
@ -122,6 +124,18 @@ func validateASTAux(
}
nextLevelInUDS = true
}
if astNode.Type == dsl.NodeTypeForLoopTwoVariable {
err := validateForLoopTwoVariableUniqueNames(astNode)
if err != nil {
return err
}
}
if astNode.Type == dsl.NodeTypeForLoopMultivariable {
err := validateForLoopMultivariableUniqueNames(astNode)
if err != nil {
return err
}
}
// Check: $-anything cannot be in begin/end
if inBeginOrEnd {
@ -241,6 +255,87 @@ func validateASTAux(
return nil
}
// Check against 'for (a, a in $*)' -- repeated 'a'.
// AST:
// * statement block
// * double-variable for-loop "for"
// * local variable "a"
// * local variable "a"
// * full record "$*"
// * statement block
func validateForLoopTwoVariableUniqueNames(astNode *dsl.ASTNode) error {
lib.InternalCodingErrorIf(astNode.Type != dsl.NodeTypeForLoopTwoVariable)
lib.InternalCodingErrorIf(len(astNode.Children) != 4)
keyVarNode := astNode.Children[0]
valVarNode := astNode.Children[1]
lib.InternalCodingErrorIf(keyVarNode.Type != dsl.NodeTypeLocalVariable)
lib.InternalCodingErrorIf(valVarNode.Type != dsl.NodeTypeLocalVariable)
keyVarName := string(keyVarNode.Token.Lit)
valVarName := string(valVarNode.Token.Lit)
if keyVarName == valVarName {
return errors.New(
fmt.Sprintf(
"%s: redefinition of variable %s in the same scope.",
os.Args[0],
keyVarName,
),
)
} else {
return nil
}
}
// Check against 'for ((a,a), b in $*)' or 'for ((a,b), a in $*)' -- repeated 'a'.
// AST:
// * statement block
// * multi-variable for-loop "for"
// * parameter list
// * local variable "a"
// * local variable "b"
// * local variable "a"
// * full record "$*"
// * statement block
func validateForLoopMultivariableUniqueNames(astNode *dsl.ASTNode) error {
lib.InternalCodingErrorIf(astNode.Type != dsl.NodeTypeForLoopMultivariable)
keyVarsNode := astNode.Children[0]
valVarNode := astNode.Children[1]
lib.InternalCodingErrorIf(keyVarsNode.Type != dsl.NodeTypeParameterList)
lib.InternalCodingErrorIf(valVarNode.Type != dsl.NodeTypeLocalVariable)
seen := make(map[string]bool)
for _, keyVarNode := range keyVarsNode.Children {
lib.InternalCodingErrorIf(keyVarNode.Type != dsl.NodeTypeLocalVariable)
name := string(keyVarNode.Token.Lit)
_, present := seen[name]
if present {
return errors.New(
fmt.Sprintf(
"%s: redefinition of variable %s in the same scope.",
os.Args[0],
name,
),
)
}
seen[name] = true
}
valVarName := string(valVarNode.Token.Lit)
if seen[valVarName] {
return errors.New(
fmt.Sprintf(
"%s: redefinition of variable %s in the same scope.",
os.Args[0],
valVarName,
),
)
}
return nil
}
// ================================================================
var VALID_LHS_NODE_TYPES = map[dsl.TNodeType]bool{
dsl.NodeTypeArrayOrMapIndexAccess: true,
dsl.NodeTypeArrayOrMapPositionalNameAccess: true,

View file

@ -26,9 +26,6 @@ bugs:
mlr put -q 'print | "tr \[a-z\] \[A-Z\]", "abi:".$a.$b.$i' s
mlr: cannot parse DSL expression.
* ./reg-test/cases/case-c-dsl-break-continue.sh
bug w/ break
* ./reg-test/cases/case-c-dsl-for-oosvar-loops.sh
bug w/ non-unique for-loop names: e.g. 'for(a,a in $*)'