func (ctx *LinkingContext) ProcessInstructionWithLocalScope(ins *tp.Instruction, scopeType int, localScope LocalDef, caller string, path string, justRoot bool) (returnType int) { returnType = -1 ins.IsValid = proto.Bool(true) switch *ins.Type { case constants.Instruction_IMPORT: importLocation := ins.GetValue() // keep track of which files we're inside of, to detect circular imports val, present := ctx.Visiting[importLocation] if present && val { ctx.error(ins, "Circular import detected: %s", importLocation) panic(fmt.Sprintf("Circular import detected: %s", importLocation)) } ctx.Visiting[importLocation] = true importId, ok := ctx.objMap[importLocation] if ok != true { ctx.error(ins, "Invalid import `%s`", ins.String()) } // if we're linking the whole tree, then recurse if !justRoot { // Make sure this object is linked with the right scopeType ctx.link(importId, scopeType) // otherwise just set the script object's scope type } else { ctx.Objects[importId].ScopeTypeId = proto.Int(scopeType) } // pop the import stack (for circular import detection) ctx.Visiting[importLocation] = false ins.ObjectId = proto.Int(importId) ins.Value = nil case constants.Instruction_LOCAL_VAR: name := null.GetString(ins.Value) if name == "1" || name == "2" || name == "3" || name == "4" || name == "5" || name == "6" || name == "7" { if len(ins.Arguments) > 0 { // We are going to assign something to this variable returnType = ctx.ProcessInstructionWithLocalScope(ins.Arguments[0], scopeType, localScope, caller, path, justRoot) if returnType != ctx.textType { ctx.error(ins, "Numeric local vars can ONLY be Text") } } if ins.Children != nil { for _, child := range ins.Children { ctx.ProcessInstructionWithLocalScope(child, ctx.textType, localScope, caller, path, justRoot) } } returnType = ctx.textType } else { // Not numeric. typeId, found := localScope[name] if found { returnType = typeId if len(ins.Arguments) > 0 { ctx.error(ins, "The local variable \"%%%s\" has been assigned before and cannot be reassigned! Open a scope on it if you need to alter the contents.", name) } else { if ins.Children != nil { for _, child := range ins.Children { returnType = ctx.ProcessInstructionWithLocalScope(child, typeId, localScope, caller, path, justRoot) } } } } else { if len(ins.Arguments) > 0 { // We are going to assign something to this variable // But first, check for possible mutation and prevent it for now. if ins.Children != nil { ctx.error(ins, "May not open a scope during initialization of local variable \"%%%s\".", name) } returnType = ctx.ProcessInstructionWithLocalScope(ins.Arguments[0], scopeType, localScope, caller, path, justRoot) localScope[name] = returnType } else { ctx.error(ins, "I've never seen the variable \"%%%s\" before! Please assign a value before usage.", name) } } } case constants.Instruction_FUNCTION_CALL: stub := null.GetString(ins.Value) if stub == "yield" { ins.YieldTypeId = proto.Int32(int32(scopeType)) } namespaces := ins.Namespaces() // ins.Namespace = nil // need to figure out where to do this step // process the args if ins.Arguments != nil { for _, arg := range ins.Arguments { argReturn := ctx.ProcessInstructionWithLocalScope(arg, scopeType, localScope, caller, path, justRoot) if argReturn == -1 { ctx.error(ins, "Invalid argument object %q", arg.String()) return } stub = stub + "," + ctx.types[argReturn] } } // for each namespace specified by the user, look up the function wrt the current context type + function name var funcId int var ok bool for _, ns := range namespaces { funcId, ok = ctx.funList[scopeType][ns+"."+stub] if ok { break } } if !ok { readableCalleeStub := readableStub(stub) readableCallerStub := readableStub(caller) location := "" if len(path) > 0 { location = path } else { location = "Package " + ctx.Pkg.GetName() } msg := fmt.Sprintf("%s:%d: function %s.%s does not exist in namespace %s", location, ins.GetLineNumber(), ctx.types[scopeType], readableCalleeStub, namespaces[0]) for i := 1; i < len(namespaces)-1; i++ { msg += ", " + namespaces[i] } if len(namespaces) > 1 { msg += " or " + namespaces[len(namespaces)-1] } msg += fmt.Sprintf("; (called from %s.%s).", ctx.types[scopeType], readableCallerStub) ctx.error(ins, msg) // message := fmt.Sprintf("Available functions in %s.%s:\n", ns, ctx.types[scopeType]) // ns := strings.SplitN(stub, ".", 2)[0] // nsPrefix := ns + "." // for funcName, _ := range ctx.funList[scopeType] { // if strings.HasPrefix(funcName, nsPrefix) { // message += "\t" + nsPrefix + readableStub(funcName) + "\n" // } // } // log.Printf("%s\n", message) // ctx.error(ins, "%s:%d: could not find function %s.%s.%s (called from %s.%s.%s)", location, ins.GetLineNumber(), ns, ctx.types[scopeType], readableCalleeStub, callerNamespace, ctx.types[scopeType], readableCallerStub) } else { ins.FunctionId = proto.Int32(int32(funcId)) fun := ctx.Pkg.Functions[funcId] returnType = int(null.GetInt32(fun.ReturnTypeId)) opensScopeType := int(null.GetInt32(fun.OpensTypeId)) if opensScopeType == 0 { // If we're a Base scope, don't mess with texas! opensScopeType = scopeType } // If it inherits: inheritedOpensScopeType := ctx.Pkg.FindDescendantType(int32(opensScopeType)) if inheritedOpensScopeType != -1 { opensScopeType = inheritedOpensScopeType } // Copy the local scope parentScope := localScope localScope = make(LocalDef, len(parentScope)) for s, t := range parentScope { localScope[s] = t } if ins.Children != nil { for _, child := range ins.Children { ctx.ProcessInstructionWithLocalScope(child, opensScopeType, localScope, stub, path, justRoot) // thread the name of the caller through the linkages } } } case constants.Instruction_TEXT: returnType = ctx.textType case constants.Instruction_BLOCK: if ins.Children != nil { for _, child := range ins.Children { returnType = ctx.ProcessInstructionWithLocalScope(child, scopeType, localScope, caller, path, justRoot) } } } return }
func printInstruction(i *tp.Instruction, indLvl int) { printIndent("Type -> %v", indLvl, i.GetType()) printIndent("Value -> %v", indLvl, i.GetValue()) printIndent("ObjectId -> %v", indLvl, i.GetObjectId()) printIndent("Function Id -> %v", indLvl, i.GetFunctionId()) printIndent("Line Number -> %v", indLvl, i.GetLineNumber()) printIndent("Yield Type Id -> %v", indLvl, i.GetYieldTypeId()) printIndent("Is Valid -> %v", indLvl, i.GetIsValid()) printIndent("Namespace -> %v", indLvl, i.GetNamespace()) printIndent("Type Qualifier -> %v", indLvl, i.GetTypeQualifier()) //printIndent("Is User Called -> %v", indLvl, i.GetIsUserCalled()) //doesn't have an accessor for some reason printIndent("Children:", indLvl) for ind, item := range i.GetChildren() { printIndent("Child[%d]:", indLvl+1, ind) printInstruction(item, indLvl+2) } printIndent("Arguments:", indLvl) for ind, item := range i.GetArguments() { printIndent("Argument[%d]:", indLvl+1, ind) printInstruction(item, indLvl+2) } }