Example #1
0
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
}
Example #2
0
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)
	}
}