Add comments and remove debug code
This commit is contained in:
		
							
								
								
									
										12
									
								
								ast.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								ast.go
									
									
									
									
									
								
							@@ -15,25 +15,36 @@ type AST struct {
 | 
				
			|||||||
// containing the position at which the error was encountered and the error
 | 
					// containing the position at which the error was encountered and the error
 | 
				
			||||||
// itself
 | 
					// itself
 | 
				
			||||||
func (ast *AST) Execute() error {
 | 
					func (ast *AST) Execute() error {
 | 
				
			||||||
 | 
						// For each command in AST
 | 
				
			||||||
	for _, cmd := range ast.Commands {
 | 
						for _, cmd := range ast.Commands {
 | 
				
			||||||
 | 
							// If parsing variables
 | 
				
			||||||
		if cmd.Vars != nil {
 | 
							if cmd.Vars != nil {
 | 
				
			||||||
 | 
								// For each variable
 | 
				
			||||||
			for _, Var := range cmd.Vars {
 | 
								for _, Var := range cmd.Vars {
 | 
				
			||||||
 | 
									// Parse value of variable
 | 
				
			||||||
				val, err := ParseValue(Var.Value)
 | 
									val, err := ParseValue(Var.Value)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return fmt.Errorf("%s: %s", Var.Value.Pos, err)
 | 
										return fmt.Errorf("%s: %s", Var.Value.Pos, err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
									// If value of variable is a function call
 | 
				
			||||||
				if IsFuncCall(val) {
 | 
									if IsFuncCall(val) {
 | 
				
			||||||
 | 
										// Assert type of val as *FuncCall
 | 
				
			||||||
					Call := val.(*FuncCall)
 | 
										Call := val.(*FuncCall)
 | 
				
			||||||
 | 
										// Set variable value to function return value
 | 
				
			||||||
					Vars[Var.Key], err = CallFunction(Call)
 | 
										Vars[Var.Key], err = CallFunction(Call)
 | 
				
			||||||
					if err != nil {
 | 
										if err != nil {
 | 
				
			||||||
						return fmt.Errorf("%s: %s", Var.Value.Pos, err)
 | 
											return fmt.Errorf("%s: %s", Var.Value.Pos, err)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
 | 
										// If value is not a function call, set variable value to parsed value
 | 
				
			||||||
					Vars[Var.Key] = val
 | 
										Vars[Var.Key] = val
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							// If parsing function calls
 | 
				
			||||||
		} else if cmd.Calls != nil {
 | 
							} else if cmd.Calls != nil {
 | 
				
			||||||
 | 
								// For each function call
 | 
				
			||||||
			for _, Call := range cmd.Calls {
 | 
								for _, Call := range cmd.Calls {
 | 
				
			||||||
 | 
									// Attempt to call function
 | 
				
			||||||
				_, err := CallFunction(Call)
 | 
									_, err := CallFunction(Call)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return fmt.Errorf("%s: %s", Call.Pos, err)
 | 
										return fmt.Errorf("%s: %s", Call.Pos, err)
 | 
				
			||||||
@@ -91,6 +102,7 @@ type Bool bool
 | 
				
			|||||||
// Capture parses a boolean literal encountered in the script into
 | 
					// Capture parses a boolean literal encountered in the script into
 | 
				
			||||||
// a Go boolean value
 | 
					// a Go boolean value
 | 
				
			||||||
func (b *Bool) Capture(values []string) error {
 | 
					func (b *Bool) Capture(values []string) error {
 | 
				
			||||||
 | 
						// Convert string to boolean
 | 
				
			||||||
	*b = values[0] == "true"
 | 
						*b = values[0] == "true"
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										25
									
								
								defaults.go
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								defaults.go
									
									
									
									
									
								
							@@ -8,47 +8,62 @@ import (
 | 
				
			|||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Default function to display a dialog
 | 
				
			||||||
func displayDialog(args map[string]interface{}) (interface{}, error) {
 | 
					func displayDialog(args map[string]interface{}) (interface{}, error) {
 | 
				
			||||||
 | 
						// Get title
 | 
				
			||||||
	title, ok := args["title"]
 | 
						title, ok := args["title"]
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return nil, errors.New("title not provided")
 | 
							return nil, errors.New("title not provided")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Get unnamed argument as text
 | 
				
			||||||
	text, ok := args[""]
 | 
						text, ok := args[""]
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return nil, errors.New("text not provided")
 | 
							return nil, errors.New("text not provided")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Display correct dialog based on given type
 | 
				
			||||||
	switch args["type"] {
 | 
						switch args["type"] {
 | 
				
			||||||
	case "yesno":
 | 
						case "yesno":
 | 
				
			||||||
 | 
							// Display yes or no dialog, returning bool based on user input
 | 
				
			||||||
		return dlgs.Question(fmt.Sprint(title), fmt.Sprint(text), true)
 | 
							return dlgs.Question(fmt.Sprint(title), fmt.Sprint(text), true)
 | 
				
			||||||
	case "info":
 | 
						case "info":
 | 
				
			||||||
 | 
							// Display info dialog, returning bool based on success
 | 
				
			||||||
		return dlgs.Info(fmt.Sprint(title), fmt.Sprint(text))
 | 
							return dlgs.Info(fmt.Sprint(title), fmt.Sprint(text))
 | 
				
			||||||
	case "error":
 | 
						case "error":
 | 
				
			||||||
 | 
							// Display error dialog, returning bool based on success
 | 
				
			||||||
		return dlgs.Error(fmt.Sprint(title), fmt.Sprint(text))
 | 
							return dlgs.Error(fmt.Sprint(title), fmt.Sprint(text))
 | 
				
			||||||
	case "entry":
 | 
						case "entry":
 | 
				
			||||||
 | 
							// Check if default text given
 | 
				
			||||||
		defaultText, ok := args["default"]
 | 
							defaultText, ok := args["default"]
 | 
				
			||||||
		if !ok {
 | 
							if !ok {
 | 
				
			||||||
 | 
								// Set to empty if not given
 | 
				
			||||||
			defaultText = ""
 | 
								defaultText = ""
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// Display entry dialog
 | 
				
			||||||
		input, _, err := dlgs.Entry(fmt.Sprint(title), fmt.Sprint(text), fmt.Sprint(defaultText))
 | 
							input, _, err := dlgs.Entry(fmt.Sprint(title), fmt.Sprint(text), fmt.Sprint(defaultText))
 | 
				
			||||||
 | 
							// Return user input
 | 
				
			||||||
		return input, err
 | 
							return input, err
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
 | 
							// If type unknown, return error
 | 
				
			||||||
		return nil, fmt.Errorf("unknown dialog type: %v", args["type"])
 | 
							return nil, fmt.Errorf("unknown dialog type: %v", args["type"])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Default function to run a shell script using `sh -c`
 | 
				
			||||||
func doShellScript(args map[string]interface{}) (interface{}, error) {
 | 
					func doShellScript(args map[string]interface{}) (interface{}, error) {
 | 
				
			||||||
 | 
						// Get unnamed argument and assert its type as string
 | 
				
			||||||
	script, ok := args[""].(string)
 | 
						script, ok := args[""].(string)
 | 
				
			||||||
 | 
						// If assertion successful
 | 
				
			||||||
	if ok {
 | 
						if ok {
 | 
				
			||||||
 | 
							// Create new exec.Cmd containing `sh -c <script>`
 | 
				
			||||||
		cmd := exec.Command("sh", "-c", script)
 | 
							cmd := exec.Command("sh", "-c", script)
 | 
				
			||||||
 | 
							// Set command I/O
 | 
				
			||||||
		cmd.Stdout = os.Stdout
 | 
							cmd.Stdout = os.Stdout
 | 
				
			||||||
 | 
							cmd.Stdin = os.Stdin
 | 
				
			||||||
 | 
							cmd.Stderr = os.Stderr
 | 
				
			||||||
 | 
							// Run command
 | 
				
			||||||
		_ = cmd.Run()
 | 
							_ = cmd.Run()
 | 
				
			||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		return nil, errors.New("script not provided")
 | 
							return nil, errors.New("script not provided")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func print(args map[string]interface{}) (interface{}, error) {
 | 
					 | 
				
			||||||
	fmt.Println(args)
 | 
					 | 
				
			||||||
	return nil, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								scpt.go
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								scpt.go
									
									
									
									
									
								
							@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					// Package scpt provides an interpreter for my simple applescript-like
 | 
				
			||||||
 | 
					// scripting language
 | 
				
			||||||
package scpt
 | 
					package scpt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@@ -20,12 +22,12 @@ type FuncMap map[string]func(map[string]interface{}) (interface{}, error)
 | 
				
			|||||||
var Funcs = FuncMap{
 | 
					var Funcs = FuncMap{
 | 
				
			||||||
	"display-dialog":  displayDialog,
 | 
						"display-dialog":  displayDialog,
 | 
				
			||||||
	"do-shell-script": doShellScript,
 | 
						"do-shell-script": doShellScript,
 | 
				
			||||||
	"print":           print,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddFuncs adds all functions from the provided FuncMap into
 | 
					// AddFuncs adds all functions from the provided FuncMap into
 | 
				
			||||||
// the Funcs variable
 | 
					// the Funcs variable
 | 
				
			||||||
func AddFuncs(fnMap FuncMap) {
 | 
					func AddFuncs(fnMap FuncMap) {
 | 
				
			||||||
 | 
						// Add each function to Funcs
 | 
				
			||||||
	for name, fn := range fnMap {
 | 
						for name, fn := range fnMap {
 | 
				
			||||||
		Funcs[name] = fn
 | 
							Funcs[name] = fn
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -35,62 +37,81 @@ func AddFuncs(fnMap FuncMap) {
 | 
				
			|||||||
// the Vars variable
 | 
					// the Vars variable
 | 
				
			||||||
func AddVars(varMap map[string]interface{}) {
 | 
					func AddVars(varMap map[string]interface{}) {
 | 
				
			||||||
	for name, val := range varMap {
 | 
						for name, val := range varMap {
 | 
				
			||||||
 | 
							// Add each variable to Vars
 | 
				
			||||||
		Vars[name] = val
 | 
							Vars[name] = val
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Parse uses participle to parse a script from r into a new AST
 | 
					// Parse uses participle to parse a script from r into a new AST
 | 
				
			||||||
func Parse(r io.Reader) (*AST, error) {
 | 
					func Parse(r io.Reader) (*AST, error) {
 | 
				
			||||||
 | 
						// Build parser from empty AST struct
 | 
				
			||||||
	parser, err := participle.Build(&AST{})
 | 
						parser, err := participle.Build(&AST{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Create new empty AST struct
 | 
				
			||||||
	ast := &AST{}
 | 
						ast := &AST{}
 | 
				
			||||||
 | 
						// Parse script from provided reader into ast
 | 
				
			||||||
	err = parser.Parse(r, ast)
 | 
						err = parser.Parse(r, ast)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Return filled AST struct
 | 
				
			||||||
	return ast, nil
 | 
						return ast, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParseValue parses a Value struct into a go value
 | 
					// ParseValue parses a Value struct into a go value
 | 
				
			||||||
func ParseValue(val *Value) (interface{}, error) {
 | 
					func ParseValue(val *Value) (interface{}, error) {
 | 
				
			||||||
 | 
						// Determine which value was provided and return it
 | 
				
			||||||
	if val.String != nil {
 | 
						if val.String != nil {
 | 
				
			||||||
 | 
							// Return unquoted string
 | 
				
			||||||
		return strings.Trim(*val.String, `"`), nil
 | 
							return strings.Trim(*val.String, `"`), nil
 | 
				
			||||||
	} else if val.Bool != nil {
 | 
						} else if val.Bool != nil {
 | 
				
			||||||
 | 
							// Return dereferenced boolean
 | 
				
			||||||
		return *val.Bool, nil
 | 
							return *val.Bool, nil
 | 
				
			||||||
	} else if val.Float != nil {
 | 
						} else if val.Float != nil {
 | 
				
			||||||
 | 
							// Return dereferenced float
 | 
				
			||||||
		return *val.Float, nil
 | 
							return *val.Float, nil
 | 
				
			||||||
	} else if val.Integer != nil {
 | 
						} else if val.Integer != nil {
 | 
				
			||||||
 | 
							// Return dereferenced integer
 | 
				
			||||||
		return *val.Integer, nil
 | 
							return *val.Integer, nil
 | 
				
			||||||
	} else if val.SubCmd != nil {
 | 
						} else if val.SubCmd != nil {
 | 
				
			||||||
 | 
							// Return reference to subcommand
 | 
				
			||||||
		return val.SubCmd, nil
 | 
							return val.SubCmd, nil
 | 
				
			||||||
	} else if val.VarVal != nil {
 | 
						} else if val.VarVal != nil {
 | 
				
			||||||
 | 
							// Return value of provided key
 | 
				
			||||||
		return Vars[*val.VarVal], nil
 | 
							return Vars[*val.VarVal], nil
 | 
				
			||||||
	} else if val.Expr != nil {
 | 
						} else if val.Expr != nil {
 | 
				
			||||||
 | 
							// Parse value of left side of expression
 | 
				
			||||||
		left, _ := ParseValue(val.Expr.Left)
 | 
							left, _ := ParseValue(val.Expr.Left)
 | 
				
			||||||
 | 
							// If value is string, requote
 | 
				
			||||||
		if isStr(left) {
 | 
							if isStr(left) {
 | 
				
			||||||
			left = requoteStr(left.(string))
 | 
								left = requoteStr(left.(string))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// Parse value of right side of expression
 | 
				
			||||||
		right, _ := ParseValue(val.Expr.Right)
 | 
							right, _ := ParseValue(val.Expr.Right)
 | 
				
			||||||
 | 
							// If value is string, requote
 | 
				
			||||||
		if isStr(right) {
 | 
							if isStr(right) {
 | 
				
			||||||
			right = requoteStr(right.(string))
 | 
								right = requoteStr(right.(string))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// Create string expression from halves and operator
 | 
				
			||||||
		exp := fmt.Sprintf(
 | 
							exp := fmt.Sprintf(
 | 
				
			||||||
			"%v %s %v",
 | 
								"%v %s %v",
 | 
				
			||||||
			left,
 | 
								left,
 | 
				
			||||||
			val.Expr.Op,
 | 
								val.Expr.Op,
 | 
				
			||||||
			right,
 | 
								right,
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
		fmt.Println(exp)
 | 
							// Compile string expression
 | 
				
			||||||
		program, err := expr.Compile(strings.ReplaceAll(exp, "^", "**"))
 | 
							program, err := expr.Compile(strings.ReplaceAll(exp, "^", "**"))
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// Run expression
 | 
				
			||||||
		out, err := expr.Run(program, Vars)
 | 
							out, err := expr.Run(program, Vars)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// Return expression output value
 | 
				
			||||||
		return out, nil
 | 
							return out, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil, nil
 | 
						return nil, nil
 | 
				
			||||||
@@ -98,11 +119,13 @@ func ParseValue(val *Value) (interface{}, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Add quotes to an unquoted string
 | 
					// Add quotes to an unquoted string
 | 
				
			||||||
func requoteStr(s string) string {
 | 
					func requoteStr(s string) string {
 | 
				
			||||||
 | 
						// Return quoted string
 | 
				
			||||||
	return `"` + s + `"`
 | 
						return `"` + s + `"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Check if i is a string
 | 
					// Check if i is a string
 | 
				
			||||||
func isStr(i interface{}) bool {
 | 
					func isStr(i interface{}) bool {
 | 
				
			||||||
 | 
						// if type of input is string, return true
 | 
				
			||||||
	if reflect.TypeOf(i).String() == "string" {
 | 
						if reflect.TypeOf(i).String() == "string" {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -113,26 +136,35 @@ func isStr(i interface{}) bool {
 | 
				
			|||||||
// storing the argument name and its value. If the argument has
 | 
					// storing the argument name and its value. If the argument has
 | 
				
			||||||
// no name, its key will be an empty string
 | 
					// no name, its key will be an empty string
 | 
				
			||||||
func UnwrapArgs(args []*Arg) (map[string]interface{}, error) {
 | 
					func UnwrapArgs(args []*Arg) (map[string]interface{}, error) {
 | 
				
			||||||
 | 
						// Create new empty map of strings to any type
 | 
				
			||||||
	argMap := map[string]interface{}{}
 | 
						argMap := map[string]interface{}{}
 | 
				
			||||||
 | 
						// For each argument
 | 
				
			||||||
	for _, arg := range args {
 | 
						for _, arg := range args {
 | 
				
			||||||
 | 
							// Parse value into interface{}
 | 
				
			||||||
		val, err := ParseValue(arg.Value)
 | 
							val, err := ParseValue(arg.Value)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// If value is function call
 | 
				
			||||||
		if IsFuncCall(val) {
 | 
							if IsFuncCall(val) {
 | 
				
			||||||
 | 
								// Call function, setting its value as the argument's value
 | 
				
			||||||
			argMap[arg.Key], err = CallFunction(val.(*FuncCall))
 | 
								argMap[arg.Key], err = CallFunction(val.(*FuncCall))
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								// Skip further code and start next loop
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// Set argument value to parsed value
 | 
				
			||||||
		argMap[arg.Key] = val
 | 
							argMap[arg.Key] = val
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Return map of arguments
 | 
				
			||||||
	return argMap, nil
 | 
						return argMap, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsFuncCall checks if val is a FuncCall struct
 | 
					// IsFuncCall checks if val is a FuncCall struct
 | 
				
			||||||
func IsFuncCall(val interface{}) bool {
 | 
					func IsFuncCall(val interface{}) bool {
 | 
				
			||||||
 | 
						// If type of val contains .FuncCall, return true
 | 
				
			||||||
	if strings.Contains(reflect.TypeOf(val).String(), ".FuncCall") {
 | 
						if strings.Contains(reflect.TypeOf(val).String(), ".FuncCall") {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -142,13 +174,16 @@ func IsFuncCall(val interface{}) bool {
 | 
				
			|||||||
// CallFunction executes a given function call in the form of
 | 
					// CallFunction executes a given function call in the form of
 | 
				
			||||||
// a FuncCall struct
 | 
					// a FuncCall struct
 | 
				
			||||||
func CallFunction(call *FuncCall) (interface{}, error) {
 | 
					func CallFunction(call *FuncCall) (interface{}, error) {
 | 
				
			||||||
 | 
						// Unwrap provided arguments
 | 
				
			||||||
	argMap, err := UnwrapArgs(call.Args)
 | 
						argMap, err := UnwrapArgs(call.Args)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Attempt to get function from Funcs map
 | 
				
			||||||
	fn, ok := Funcs[call.Name]
 | 
						fn, ok := Funcs[call.Name]
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return nil, errors.New("no such function: " + call.Name)
 | 
							return nil, errors.New("no such function: " + call.Name)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Return value received from function
 | 
				
			||||||
	return fn(argMap)
 | 
						return fn(argMap)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user