func AssignToScopedObjectKey(objectName string, keyName string, value Value, lineno int) { if _, in1 := Scope[objectName]; !in1 { if _, in2 := GlobalScope[objectName]; !in2 { // Not in either local or global log.UndeclaredVariable(lineno, objectName) return } else { // In global gObject := GetVariable(objectName, lineno) if gObject.Type != VALUE_OBJECT { log.TypeViolation(lineno) return } if value.Type == VALUE_OBJECT || value.Type == VALUE_ARRAY { log.TypeViolation(lineno) return } gObject.Value.(map[string]Value)[keyName] = Value{Type: value.Type, Value: value.Value, Line: value.Line, Written: true} } } else { lObject := GetVariable(objectName, lineno) if lObject.Type != VALUE_OBJECT { log.TypeViolation(lineno) return } if value.Type == VALUE_OBJECT || value.Type == VALUE_ARRAY { log.TypeViolation(lineno) return } lObject.Value.(map[string]Value)[keyName] = Value{Type: value.Type, Value: value.Value, Line: value.Line, Written: true} } }
func AssignToScopedArrayIndex(arrayName string, indexNo int, value Value, lineno int) { if _, in1 := Scope[arrayName]; !in1 { if _, in2 := GlobalScope[arrayName]; !in2 { log.UndeclaredVariable(lineno, arrayName) return } else { gArray := GetVariable(arrayName, lineno) if gArray.Type != VALUE_ARRAY { log.TypeViolation(lineno) return } if value.Type == VALUE_OBJECT || value.Type == VALUE_ARRAY { log.TypeViolation(lineno) return } gArray.Value.(map[string]Value)[strconv.Itoa(indexNo)] = Value{Type: value.Type, Value: value.Value, Line: value.Line, Written: true} } } else { lArray := GetVariable(arrayName, lineno) if lArray.Type != VALUE_ARRAY { log.TypeViolation(lineno) return } if value.Type == VALUE_ARRAY || value.Type == VALUE_OBJECT { log.TypeViolation(lineno) return } lArray.Value.(map[string]Value)[strconv.Itoa(indexNo)] = Value{Type: value.Type, Value: value.Value, Line: value.Line, Written: true} } }
func handlePlus(left Value, right Value, line int) Value { // Integers if left.Type == VALUE_INT && right.Type == VALUE_INT { left.Value = left.Value.(int) + right.Value.(int) return left } // Strings if left.Type == VALUE_STRING && right.Type == VALUE_STRING { lStr := left.Value.(string) rStr := right.Value.(string) if strings.Contains(lStr, "<br />") || strings.Contains(rStr, "<br />") { log.TypeViolation(line) left.Type = VALUE_UNDEFINED return left } left.Value = left.Value.(string) + right.Value.(string) return left } log.TypeViolation(line) left.Type = VALUE_UNDEFINED return left }
func handleNot(v Value, line int) Value { if v.Type == VALUE_BOOLEAN { v.Value = !v.Value.(bool) return v } if v.Type == VALUE_INT { if v.Value.(int) == 0 { v.Type = VALUE_BOOLEAN v.Value = true } else { v.Type = VALUE_BOOLEAN v.Value = false } return v } if v.Type == VALUE_STRING { if len(v.Value.(string)) == 0 { v.Type = VALUE_BOOLEAN v.Value = true } else { v.Type = VALUE_BOOLEAN v.Value = false } return v } log.TypeViolation(line) v.Type = VALUE_UNDEFINED return v }
func (a Assignment) Execute() interface{} { rhsResult := a.Rhs.Execute() // The type of the right side should always be a Value // This line is included just to throw an error if it ever isn't, which is // mainly for debugging rightValue := rhsResult.(Value) switch a.Type { case VAR_NORM: AssignToVariable(a.Name, rightValue, a.Line) case VAR_OBJECT: AssignToObjectKey(a.Name, a.ObjChild, rightValue, a.Line) case VAR_ARRAY: // Check to ensure the index is an int: otherwise type error index := a.Index.Execute().(Value) if index.Type != VALUE_INT { log.TypeViolation(a.Line) return nil } AssignToArrayIndex(a.Name, index.Value.(int), rightValue, a.Line) } return nil }
func handleOr(left Value, right Value, line int) Value { // Convert the type of the left and right side if they arent boolean if left.Type == VALUE_INT { left.Type = VALUE_BOOLEAN left.Value = left.Value.(int) > 0 } if right.Type == VALUE_INT { right.Type = VALUE_BOOLEAN right.Value = right.Value.(int) > 0 } if left.Type == VALUE_STRING { left.Type = VALUE_BOOLEAN left.Value = len(left.Value.(string)) > 0 } if right.Type == VALUE_STRING { right.Type = VALUE_BOOLEAN right.Value = len(right.Value.(string)) > 0 } // Booleans if left.Type == VALUE_BOOLEAN && right.Type == VALUE_BOOLEAN { left.Value = left.Value.(bool) || right.Value.(bool) return left } log.TypeViolation(line) left.Type = VALUE_UNDEFINED return left }
func handleNequiv(left Value, right Value, line int) Value { // Integers if left.Type == VALUE_INT && right.Type == VALUE_INT { left.Type = VALUE_BOOLEAN left.Value = left.Value.(int) != right.Value.(int) return left } // Booleans if left.Type == VALUE_BOOLEAN && right.Type == VALUE_BOOLEAN { left.Value = left.Value.(bool) != right.Value.(bool) return left } // Strings if left.Type == VALUE_STRING && right.Type == VALUE_STRING { left.Type = VALUE_BOOLEAN left.Value = left.Value.(string) != right.Value.(string) return left } log.TypeViolation(line) left.Type = VALUE_UNDEFINED return left }
func DocumentWrite(fc FunctionCall) interface{} { for _, arg := range fc.Args { argv := arg.Execute().(Value) fmt.Print(argv.ToString()) switch argv.Type { case VALUE_OBJECT: if !log.EXTENSIONS { log.TypeViolation(fc.LineNo()) } case VALUE_ARRAY: if !log.EXTENSIONS { log.TypeViolation(fc.LineNo()) } } } return nil }
func (f FunctionCall) Execute() interface{} { log.Trace("ast", "Executing function "+f.Name) // Create the local stack and return val f.LocalScope = make(map[string]Value) f.ReturnVal = Value{Type: VALUE_UNDEFINED, Line: f.Line} // Retrieve the function definition funVal := GetVariable(f.Name, f.Line) if funVal.Type != VALUE_FUNCTION { log.TypeViolation(f.Line) return f.ReturnVal } funDef := funVal.Value.(FunctionDef) // Check number of arguments if !funDef.ArbitraryArgs && len(f.Args) != len(funDef.ArgNames) { log.TypeViolation(f.Line) return f.ReturnVal } if funDef.ExecMiniscript { // Load the arguments into the local stack for i, name := range funDef.ArgNames { f.LocalScope[name] = f.Args[i].Execute().(Value) } // Save the old scope and set this local as its own scope oldScope := Scope Scope = f.LocalScope // Execute the function retVal := funDef.MSBody.Execute() var nRetVal Value switch retVal.(type) { case Return: nRetVal = retVal.(Return).Value.Execute().(Value) case Value: nRetVal = retVal.(Value) } // Restore the old scope Scope = oldScope return nRetVal } else { funDef.GoBody(f) return nil } }
func AssignToGlobalObjectKey(objectName string, keyName string, value Value, lineno int) { if _, in := GlobalScope[objectName]; !in { log.UndeclaredVariable(lineno, objectName) } // Get the object from the symbol table object := GetVariable(objectName, lineno) if object.Type != VALUE_OBJECT { log.TypeViolation(lineno) return } // If we are assigning an object or array, fail if value.Type == VALUE_OBJECT || value.Type == VALUE_ARRAY { log.TypeViolation(lineno) return } object.Value.(map[string]Value)[keyName] = Value{Type: value.Type, Value: value.Value, Line: value.Line, Written: true} }
func handleDivide(left Value, right Value, line int) Value { // Integers if left.Type == VALUE_INT && right.Type == VALUE_INT { left.Value = left.Value.(int) / right.Value.(int) return left } log.TypeViolation(line) left.Type = VALUE_UNDEFINED return left }
func (a Assert) Execute() interface{} { // Execute the value result := a.Value.Execute() // If its not a value, type error switch result.(type) { case Value: default: log.TypeViolation(a.Line) } // Evalute the value to a boolean bVal := result.(Value).ToBoolean() // Exit the program if it evaluates to false if !bVal.Value.(bool) { os.Exit(1) } return nil }
func (vr Reference) Execute() interface{} { switch vr.Type { case VAR_NORM: return GetVariable(vr.Name, vr.LineNo()) case VAR_OBJECT: return GetObjectMember(vr.Name, vr.ObjChild, vr.LineNo()) case VAR_ARRAY: // Check to ensure the index is an int: otherwise type error index := vr.Index.Execute().(Value) if index.Type != VALUE_INT { log.TypeViolation(vr.Line) return Value{Type: VALUE_UNDEFINED, Line: vr.Line} } return GetArrayMember(vr.Name, index.Value.(int), vr.LineNo()) default: panic("Bad variable reference type") } return nil }
func GetArrayMember(arrayName string, index int, lineno int) Value { // Find the array itself array := GetVariable(arrayName, lineno) if array.Type == VALUE_UNDEFINED { log.ValueError(lineno, arrayName) } if array.Type != VALUE_ARRAY { log.TypeViolation(lineno) return Value{Type: VALUE_UNDEFINED, Line: lineno} } oldScope := Scope Scope = array.Value.(map[string]Value) keyValue := GetVariableGeneric(strconv.Itoa(index), fmt.Sprintf("%s[%d]", arrayName, index), lineno) Scope = oldScope return Value{Type: keyValue.Type, Value: keyValue.Value, Written: keyValue.Written, Line: keyValue.Line} }
func GetObjectMember(objectName string, keyName string, lineno int) Value { // Find the object itself object := GetVariable(objectName, lineno) if object.Type == VALUE_UNDEFINED { log.ValueError(lineno, objectName) } if object.Type != VALUE_OBJECT { log.TypeViolation(lineno) return Value{Type: VALUE_UNDEFINED, Line: lineno} } // Set this object's fields as the new scope then restore it later // This is, quite possibly, the smartest thing I have ever devised. Lol. Lol. Lol. // Full of himself meter = 10/10. Chance of anyone ever seeing this: 1/10 oldScope := Scope Scope = object.Value.(map[string]Value) keyValue := GetVariableGeneric(keyName, objectName+"."+keyName, lineno) Scope = oldScope // All other error handling is done in gvg() return Value{Type: keyValue.Type, Value: keyValue.Value, Written: keyValue.Written, Line: keyValue.Line} }
func (be *BinaryExpression) Execute() interface{} { log.Tracef("ast", "Executing binary expression %s", be.Op) // Execute the left side left := be.Lhs.Execute().(Value) // Error check the response from the left side if left.Type == VALUE_UNDEFINED && !left.Written { log.TypeViolation(be.Line) left.Type = VALUE_UNDEFINED return left } // if left.Type == VALUE_UNDEFINED { left.Type = VALUE_UNDEFINED return left } // Short circuit the right side var right Value leftBool := left.ToBoolean() if be.Op == "||" && leftBool.Value.(bool) { right = Value{Type: VALUE_BOOLEAN, Value: true, Line: left.LineNo(), Written: left.Written} } else if be.Op == "&&" && !leftBool.Value.(bool) { right = Value{Type: VALUE_BOOLEAN, Value: false, Line: left.LineNo(), Written: left.Written} } else { right = be.Rhs.Execute().(Value) } // Error check the right side now if right.Type == VALUE_UNDEFINED && !right.Written { log.TypeViolation(be.Line) left.Type = VALUE_UNDEFINED return left } // If one side is undefined and written, we just return undefined if right.Type == VALUE_UNDEFINED { left.Type = VALUE_UNDEFINED return left } // If the types are simply not the same and none are undefined, we report a type violation and return undefined // We also dont do this for && and || (truthy) operations b if (left.Type != right.Type) && (!opIsCoersive(be.Op)) { log.TypeViolation(be.Line) left.Type = VALUE_UNDEFINED return left } // Handle each operation separately switch be.Op { case "+": return handlePlus(left, right, be.Line) case "-": return handleMinus(left, right, be.Line) case "*": return handleMult(left, right, be.Line) case "/": return handleDivide(left, right, be.Line) case "==": return handleEquiv(left, right, be.Line) case "!=": return handleNequiv(left, right, be.Line) case "||": return handleOr(left, right, be.Line) case "&&": return handleAnd(left, right, be.Line) case ">": return handleGt(left, right, be.Line) case "<": return handleLt(left, right, be.Line) case ">=": return handleGte(left, right, be.Line) case "<=": return handleLte(left, right, be.Line) } // This should never be reached panic("You just supplied a binary operator we dont support") }