From 3650db430f00324810b33b01fd313074cc43c32e Mon Sep 17 00:00:00 2001 From: oysheng <33340252+oysheng@users.noreply.github.com> Date: Thu, 27 Dec 2018 16:23:17 +0800 Subject: [PATCH] add the replacement of defined variable for AST and adjusting the structural format (#33) * add paramenters for function argment expression * modify struct * optimise * replace the temporary variable for its expression * optimise * optimise code for expression * adjusting syntactic format * optimise lock statements expression * fix the assign expression * optimise equitycmd * modify json name for blockheight --- compiler/ast.go | 57 +++++++++++----- compiler/checks.go | 129 +++++++++++++++++++++++++++--------- compiler/cmd/equitycmd/equitycmd.go | 2 +- compiler/compile.go | 8 +-- 4 files changed, 142 insertions(+), 54 deletions(-) diff --git a/compiler/ast.go b/compiler/ast.go index fba7f21..f5f91d0 100644 --- a/compiler/ast.go +++ b/compiler/ast.go @@ -63,9 +63,8 @@ type Clause struct { statements []statement - // BlockHeight is the list of expressions passed to greater()/less() in this - // clause. - BlockHeight []string `json:"blockheight,omitempty"` + // BlockHeight is the list of expressions passed to above()/below() in this clause. + BlockHeight []string `json:"block_height,omitempty"` // HashCalls is the list of hash functions and their arguments used // in this clause. @@ -74,13 +73,9 @@ type Clause struct { // Values is the list of values unlocked or relocked in this clause. Values []ValueInfo `json:"values"` - // Conditions is the list of condition for if-else statements which body contains - // the lock or unlock statement in this clause. - Conditions map[string]Condition `json:"conditions"` - - // CondValues is the map of values unlocked or relocked in this clause's + // CondValues is the list of condition values unlocked or relocked in this clause's // if-else statements which body contains the lock or unlock statement. - CondValues map[string][]ValueInfo `json:"cond_values"` + CondValues []CondValueInfo `json:"cond_values"` // Contracts is the list of contracts called by this clause. Contracts []string `json:"contracts,omitempty"` @@ -106,9 +101,12 @@ type ValueInfo struct { // the contract value instead, this is empty. Amount string `json:"amount,omitempty"` - // Params is the list of parameters for amount expression. If the value - // of amount is a variable, this is empty. - Params []*Param `json:"params,omitempty"` + // AmountParams is the list of parameters for Amount expression. + // If the value of amount is a variable, this is empty. + AmountParams []*Param `json:"amount_params,omitempty"` + + // ContractCalls is the list of arguments for program which is a contract. + ContractCalls []CallArgs `json:"contract_calls,omitempty"` } // HashCall describes a call to a hash function. @@ -123,12 +121,39 @@ type HashCall struct { ArgType string `json:"arg_type"` } -// Condition describes a condition expression. -type Condition struct { - // Source is the string format of condition expression. +// CondValueInfo describes a struct for if-else statements which body +// contains the lock or unlock statement. +type CondValueInfo struct { + // condition is the condition expression for if-else statements. + Condition ExpressionInfo `json:"condition"` + + // TrueBodyValues is the list of values unlocked or relocked in the trueBody + // for if-else statements. + TrueBodyValues []ValueInfo `json:"true_body"` + + // FalseBodyValues is the list of values unlocked or relocked in the falseBody + // for if-else statements. + FalseBodyValues []ValueInfo `json:"false_body"` +} + +// ExpressionInfo describes a operational expression. +type ExpressionInfo struct { + // Source is the string format of operational expression. Source string `json:"source"` - // Params is the list of parameters for condition expression. + // Params is the list of parameters for operational expression. + Params []*Param `json:"params,omitempty"` +} + +// CallArgs describes a argument expression for function or contract call. +type CallArgs struct { + // Source is the string format of argument expression. + Source string `json:"source"` + + // Position is the position of argument expression. + Position int `json:"position"` + + // Params is the list of parameters for argument expression. Params []*Param `json:"params,omitempty"` } diff --git a/compiler/checks.go b/compiler/checks.go index 2a4d663..0eda118 100644 --- a/compiler/checks.go +++ b/compiler/checks.go @@ -1,6 +1,9 @@ package compiler -import "fmt" +import ( + "fmt" + "strings" +) func checkRecursive(contract *Contract) bool { for _, clause := range contract.Clauses { @@ -39,63 +42,95 @@ func checkStatRecursive(stmt statement, contractName string) bool { return false } -func calClauseValues(contract *Contract, env *environ, stmt statement, conditions map[string]Condition, condValues map[string][]ValueInfo, index *int) (valueInfo *ValueInfo) { +func calClauseValues(contract *Contract, env *environ, stmt statement, condValues *[]CondValueInfo, tempVariables map[string]ExpressionInfo) (valueInfo *ValueInfo) { switch s := stmt.(type) { case *ifStatement: - *index++ - strIndex := fmt.Sprintf("%d", *index) - conditionCounts := make(map[string]int) s.condition.countVarRefs(conditionCounts) + condExpr := s.condition.String() + params := getParams(env, conditionCounts, &condExpr, tempVariables) + condition := ExpressionInfo{Source: condExpr, Params: params} - params := []*Param{} - for v := range conditionCounts { - if entry := env.lookup(v); entry != nil && (entry.r == roleContractParam || entry.r == roleContractValue || entry.r == roleClauseParam || entry.r == roleClauseVariable) { - params = append(params, &Param{Name: v, Type: entry.t}) - } - } - - condition := Condition{Source: s.condition.String(), Params: params} - conditions["condition_"+strIndex] = condition - - trueValues := []ValueInfo{} + var trueValues []ValueInfo for _, trueStmt := range s.body.trueBody { - trueValue := calClauseValues(contract, env, trueStmt, conditions, condValues, index) + var trueValue *ValueInfo + trueValue = calClauseValues(contract, env, trueStmt, condValues, tempVariables) if trueValue != nil { trueValues = append(trueValues, *trueValue) } } - condValues["truebody_"+strIndex] = trueValues + var falseValues []ValueInfo if len(s.body.falseBody) != 0 { - falseValues := []ValueInfo{} for _, falseStmt := range s.body.falseBody { - falseValue := calClauseValues(contract, env, falseStmt, conditions, condValues, index) + var falseValue *ValueInfo + falseValue = calClauseValues(contract, env, falseStmt, condValues, tempVariables) if falseValue != nil { falseValues = append(falseValues, *falseValue) } } - condValues["falsebody_"+strIndex] = falseValues } + condValue := CondValueInfo{Condition: condition, TrueBodyValues: trueValues, FalseBodyValues: falseValues} + *condValues = append([]CondValueInfo{condValue}, *condValues...) - case *lockStatement: - valueInfo = &ValueInfo{ - Amount: s.lockedAmount.String(), - Asset: s.lockedAsset.String(), - Program: s.program.String(), + case *defineStatement: + if s.expr != nil { + defineCounts := make(map[string]int) + s.expr.countVarRefs(defineCounts) + defineExpr := s.expr.String() + params := getParams(env, defineCounts, &defineExpr, tempVariables) + tempVariables[s.variable.Name] = ExpressionInfo{Source: defineExpr, Params: params} } + case *assignStatement: + assignCounts := make(map[string]int) + s.expr.countVarRefs(assignCounts) + assignExpr := s.expr.String() + params := getParams(env, assignCounts, &assignExpr, tempVariables) + tempVariables[s.variable.Name] = ExpressionInfo{Source: assignExpr, Params: params} + + case *lockStatement: + valueInfo = &ValueInfo{Asset: s.lockedAsset.String()} lockCounts := make(map[string]int) s.lockedAmount.countVarRefs(lockCounts) - if _, ok := lockCounts[s.lockedAmount.String()]; !ok { - params := []*Param{} - for v := range lockCounts { - if entry := env.lookup(v); entry != nil && (entry.r == roleContractParam || entry.r == roleContractValue || entry.r == roleClauseParam || entry.r == roleClauseVariable) { - params = append(params, &Param{Name: v, Type: entry.t}) + lockedAmountExpr := s.lockedAmount.String() + if _, ok := lockCounts[lockedAmountExpr]; !ok { + valueInfo.AmountParams = getParams(env, lockCounts, &lockedAmountExpr, tempVariables) + } else if _, ok := tempVariables[lockedAmountExpr]; ok { + valueInfo.AmountParams = tempVariables[lockedAmountExpr].Params + lockedAmountExpr = tempVariables[lockedAmountExpr].Source + } + valueInfo.Amount = lockedAmountExpr + + programExpr := s.program.String() + if res, ok := s.program.(*callExpr); ok { + if bi := referencedBuiltin(res.fn); bi == nil { + if v, ok := res.fn.(varRef); ok { + if entry := env.lookup(string(v)); entry != nil && entry.t == contractType { + programExpr = fmt.Sprintf("%s(", string(v)) + for i := 0; i < len(res.args); i++ { + argExpr := res.args[i].String() + argCounts := make(map[string]int) + res.args[i].countVarRefs(argCounts) + if _, ok := argCounts[argExpr]; !ok { + params := getParams(env, argCounts, &argExpr, tempVariables) + valueInfo.ContractCalls = append(valueInfo.ContractCalls, CallArgs{Source: argExpr, Position: i, Params: params}) + } else if _, ok := tempVariables[argExpr]; ok { + valueInfo.ContractCalls = append(valueInfo.ContractCalls, CallArgs{Source: tempVariables[argExpr].Source, Position: i, Params: tempVariables[argExpr].Params}) + argExpr = tempVariables[argExpr].Source + } + + if i == len(res.args)-1 { + programExpr = fmt.Sprintf("%s%s)", programExpr, argExpr) + } else { + programExpr = fmt.Sprintf("%s%s, ", programExpr, argExpr) + } + } + } } } - valueInfo.Params = params } + valueInfo.Program = programExpr case *unlockStatement: valueInfo = &ValueInfo{ @@ -107,6 +142,36 @@ func calClauseValues(contract *Contract, env *environ, stmt statement, condition return valueInfo } +func getParams(env *environ, counts map[string]int, expr *string, tempVariables map[string]ExpressionInfo) (params []*Param) { + for v := range counts { + if entry := env.lookup(v); entry != nil && (entry.r == roleContractParam || entry.r == roleContractValue || entry.r == roleClauseParam) { + params = append(params, &Param{Name: v, Type: entry.t}) + } else if entry.r == roleClauseVariable { + if expr != nil { + *expr = strings.Replace(*expr, v, tempVariables[v].Source, -1) + } + + if _, ok := tempVariables[v]; ok { + for _, param := range tempVariables[v].Params { + if ok := checkParams(param, params); !ok { + params = append(params, &Param{Name: param.Name, Type: param.Type}) + } + } + } + } + } + return params +} + +func checkParams(param *Param, params []*Param) bool { + for _, p := range params { + if p.Name == param.Name { + return true + } + } + return false +} + func prohibitSigParams(contract *Contract) error { for _, p := range contract.Params { if p.Type == sigType { diff --git a/compiler/cmd/equitycmd/equitycmd.go b/compiler/cmd/equitycmd/equitycmd.go index a567fa2..b48221b 100644 --- a/compiler/cmd/equitycmd/equitycmd.go +++ b/compiler/cmd/equitycmd/equitycmd.go @@ -90,7 +90,7 @@ func main() { fmt.Fprintf(buf, "}\n\n") for _, contract := range contracts { - fmt.Fprintf(buf, "// contract %s(%s) locks %s\n", contract.Name, paramsStr(contract.Params), contract.Value) + fmt.Fprintf(buf, "// contract %s(%s) locks %s of %s\n", contract.Name, paramsStr(contract.Params), contract.Value.Amount, contract.Value.Asset) fmt.Fprintf(buf, "//\n") maxWidth := 0 for _, step := range contract.Steps { diff --git a/compiler/compile.go b/compiler/compile.go index 778d138..62aa4e3 100644 --- a/compiler/compile.go +++ b/compiler/compile.go @@ -279,15 +279,13 @@ func compileClause(b *builder, contractStk stack, contract *Contract, env *envir } } - conditions := make(map[string]Condition) - condValues := make(map[string][]ValueInfo) - index := 0 + var condValues []CondValueInfo + tempVariables := map[string]ExpressionInfo{} for _, stmt := range clause.statements { - valueInfo := calClauseValues(contract, env, stmt, conditions, condValues, &index) + valueInfo := calClauseValues(contract, env, stmt, &condValues, tempVariables) if valueInfo != nil { clause.Values = append(clause.Values, *valueInfo) } else { - clause.Conditions = conditions clause.CondValues = condValues } } -- 2.11.0