func AssignToGlobalVariable(name string, value Value, lineno int) { log.Tracef("sym", "Assigning value %s to global variable %s", value.ToString(), name) if _, in := GlobalScope[name]; !in { log.UndeclaredVariable(lineno, name) } GlobalScope[name] = Value{Type: value.Type, Value: value.Value, Line: value.Line, Written: true} }
func AssignToScopedVariable(name string, value Value, lineno int) { log.Tracef("sym", "Assigning value %s to scope-local variable %s", value.ToString(), name) if _, in1 := Scope[name]; !in1 { if _, in2 := GlobalScope[name]; !in2 { log.UndeclaredVariable(lineno, name) Scope[name] = Value{Type: value.Type, Value: value.Value, Line: value.Line, Written: true} } else { GlobalScope[name] = Value{Type: value.Type, Value: value.Value, Line: value.Line, Written: true} } } else { Scope[name] = Value{Type: value.Type, Value: value.Value, Line: value.Line, Written: true} } }
func (ue UnaryExpression) Execute() interface{} { log.Tracef("ast", "Executing binary expression %s", ue.Op) // Execute the value value := ue.Value.Execute().(Value) // Switch on the operator switch ue.Op { case "!": return handleNot(value, ue.Line) } panic("Supplied a unary operator not supported") }
func GetVariable(varName string, lineno int) Value { log.Tracef("sym", "Getting variable %s", varName) return GetVariableGeneric(varName, varName, lineno) }
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") }