OSDN Git Service

add assgin statement and modify defined statement to support declaration (#9)
authoroysheng <33340252+oysheng@users.noreply.github.com>
Tue, 18 Sep 2018 07:31:55 +0000 (15:31 +0800)
committerPaladz <yzhu101@uottawa.ca>
Tue, 18 Sep 2018 07:31:55 +0000 (15:31 +0800)
* modify define statement

* add assgin statement

* optimise assign statement

* modify print

* add assign statement unit test

* not support that the variable was been used in assign statement when it's not defined

* check whether the defined variable is used or not

compiler/ast.go
compiler/checks.go
compiler/compile.go
compiler/compile_test.go
compiler/equitytest/equitytest.go
compiler/parse.go

index b896fcc..41f9e6a 100644 (file)
@@ -104,14 +104,23 @@ type statement interface {
 }
 
 type defineStatement struct {
-       varName *Param
-       expr    expression
+       variable *Param
+       expr     expression
 }
 
 func (s defineStatement) countVarRefs(counts map[string]int) {
        s.expr.countVarRefs(counts)
 }
 
+type assignStatement struct {
+       variable *Param
+       expr     expression
+}
+
+func (s assignStatement) countVarRefs(counts map[string]int) {
+       s.expr.countVarRefs(counts)
+}
+
 type ifStatement struct {
        condition expression
        body      *IfStatmentBody
index b9720af..a705137 100644 (file)
@@ -105,6 +105,8 @@ func checkParamUsedInStatement(param *Param, stmt statement) (used bool) {
 
        case *defineStatement:
                used = references(s.expr, param.Name)
+       case *assignStatement:
+               used = references(s.expr, param.Name)
        case *verifyStatement:
                used = references(s.expr, param.Name)
        case *lockStatement:
@@ -279,9 +281,15 @@ func typeCheckStatement(stat statement, contractValue ValueInfo, clauseName stri
                }
 
        case *defineStatement:
-               if stmt.expr.typ(env) != stmt.varName.Type && !isHashSubtype(stmt.expr.typ(env)) {
+               if stmt.expr != nil && stmt.expr.typ(env) != stmt.variable.Type && !isHashSubtype(stmt.expr.typ(env)) {
                        return fmt.Errorf("expression in define statement in clause \"%s\" has type \"%s\", must be \"%s\"",
-                               clauseName, stmt.expr.typ(env), stmt.varName.Type)
+                               clauseName, stmt.expr.typ(env), stmt.variable.Type)
+               }
+
+       case *assignStatement:
+               if stmt.expr.typ(env) != stmt.variable.Type && !isHashSubtype(stmt.expr.typ(env)) {
+                       return fmt.Errorf("expression in assign statement in clause \"%s\" has type \"%s\", must be \"%s\"",
+                               clauseName, stmt.expr.typ(env), stmt.variable.Type)
                }
 
        case *verifyStatement:
index b9199f2..b367af5 100644 (file)
@@ -321,6 +321,10 @@ func compileClause(b *builder, contractStk stack, contract *Contract, env *envir
        // a count of the number of times each variable is referenced
        counts := make(map[string]int)
        for _, s := range clause.statements {
+               if stmt, ok := s.(*defineStatement); ok && stmt.expr == nil {
+                       continue
+               }
+
                s.countVarRefs(counts)
                if stmt, ok := s.(*ifStatement); ok {
                        for _, trueStmt := range stmt.body.trueBody {
@@ -449,19 +453,74 @@ func compileStatement(b *builder, stk stack, contract *Contract, env *environ, c
                b.addJumpTarget(stk, "endif_"+strSequence)
 
        case *defineStatement:
+               // add environ for define variable
+               if err = env.add(stmt.variable.Name, stmt.variable.Type, roleClauseVariable); err != nil {
+                       return stk, err
+               }
+
+               // check whether the variable is used or not
+               if counts[stmt.variable.Name] == 0 {
+                       return stk, fmt.Errorf("the defined variable \"%s\" is unused in clause \"%s\"", stmt.variable.Name, clause.Name)
+               }
+
+               if stmt.expr != nil {
+                       // variable
+                       stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
+                       if err != nil {
+                               return stk, errors.Wrapf(err, "in define statement in clause \"%s\"", clause.Name)
+                       }
+
+                       // modify stack name
+                       stk.str = stmt.variable.Name
+               }
+
+       case *assignStatement:
+               // find variable from environ with roleClauseVariable
+               if entry := env.lookup(string(stmt.variable.Name)); entry != nil {
+                       if entry.r != roleClauseVariable {
+                               return stk, fmt.Errorf("the type of variable is not roleClauseVariable in assign statement in clause \"%s\"", clause.Name)
+                       }
+                       stmt.variable.Type = entry.t
+               } else {
+                       return stk, fmt.Errorf("the variable \"%s\" is not defined before the assign statement in clause \"%s\"", stmt.variable.Name, clause.Name)
+               }
+
+               // temporary store the counts of defined variable
+               varCount := counts[stmt.variable.Name]
+
+               // calculate the counts of variable for assign statement
+               tmpCounts := make(map[string]int)
+               stmt.countVarRefs(tmpCounts)
+
+               // modify the map counts of defined variable to 1 and minus the number of defined variable
+               // when the assign expression contains the defined variable
+               if tmpCounts[stmt.variable.Name] > 0 {
+                       counts[stmt.variable.Name] = 1
+                       varCount -= tmpCounts[stmt.variable.Name]
+               } else {
+                       depth := stk.find(stmt.variable.Name)
+                       switch depth {
+                       case 0:
+                               break
+                       case 1:
+                               stk = b.addSwap(stk)
+                       default:
+                               stk = b.addRoll(stk, depth)
+                       }
+                       stk = b.addDrop(stk)
+               }
+
                // variable
                stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
                if err != nil {
                        return stk, errors.Wrapf(err, "in define statement in clause \"%s\"", clause.Name)
                }
 
-               // modify stack name
-               stk.str = stmt.varName.Name
+               // restore the defined variable counts
+               counts[stmt.variable.Name] = varCount
 
-               // add environ for define variable
-               if err = env.add(stmt.varName.Name, stmt.varName.Type, roleClauseVariable); err != nil {
-                       return stk, err
-               }
+               // modify stack name
+               stk.str = stmt.variable.Name
 
        case *verifyStatement:
                stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
index f1ff092..a55dc02 100644 (file)
@@ -67,6 +67,11 @@ func TestCompile(t *testing.T) {
                        "52797b937b7887916987",
                },
                {
+                       "TestAssignVar",
+                       equitytest.TestAssignVar,
+                       "7b7b9387",
+               },
+               {
                        "TestSigIf",
                        equitytest.TestSigIf,
                        "53797b879169765379a00087641c00000052795279a0696321000000765279a069",
index 701662f..e362da2 100644 (file)
@@ -102,6 +102,17 @@ contract TestDefineVar(result: Integer) locks valueAmount of valueAsset {
 }
 `
 
+const TestAssignVar = `
+contract TestAssignVar(result: Integer) locks valueAmount of valueAsset {
+  clause LockWithMath(first: Integer, second: Integer) {
+    define calculate: Integer = first
+    assign calculate = calculate + second
+    verify result == calculate
+    unlock valueAmount of valueAsset
+  }
+}
+`
+
 const TestSigIf = `
 contract TestSigIf(a: Integer, count:Integer) locks valueAmount of valueAsset {
   clause check(b: Integer, c: Integer) {
index b773164..3dcb4bb 100644 (file)
@@ -145,6 +145,8 @@ func parseStatement(p *parser) statement {
                return parseIfStmt(p)
        case "define":
                return parseDefineStmt(p)
+       case "assign":
+               return parseAssignStmt(p)
        case "verify":
                return parseVerifyStmt(p)
        case "lock":
@@ -172,13 +174,31 @@ func parseIfStmt(p *parser) *ifStatement {
 }
 
 func parseDefineStmt(p *parser) *defineStatement {
+       defineStat := &defineStatement{}
        consumeKeyword(p, "define")
-       variableName := consumeIdentifier(p)
+       param := &Param{}
+       param.Name = consumeIdentifier(p)
        consumeTok(p, ":")
        variableType := consumeIdentifier(p)
+       if tdesc, ok := types[variableType]; ok {
+               param.Type = tdesc
+       } else {
+               p.errorf("unknown type %s", variableType)
+       }
+       defineStat.variable = param
+       if peekTok(p, "=") {
+               consumeTok(p, "=")
+               defineStat.expr = parseExpr(p)
+       }
+       return defineStat
+}
+
+func parseAssignStmt(p *parser) *assignStatement {
+       consumeKeyword(p, "assign")
+       varName := consumeIdentifier(p)
        consumeTok(p, "=")
        expr := parseExpr(p)
-       return &defineStatement{varName: &Param{Name: variableName, Type: typeDesc(variableType)}, expr: expr}
+       return &assignStatement{variable: &Param{Name: varName}, expr: expr}
 }
 
 func parseVerifyStmt(p *parser) *verifyStatement {