improve error messages a bit more
This commit is contained in:
parent
3e4df77541
commit
dcd0db36eb
23
main/error.go
Normal file
23
main/error.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type FermiError struct {
|
||||
Code string
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e FermiError) Error() string {
|
||||
return e.Msg
|
||||
}
|
||||
|
||||
func PrintError(e error) {
|
||||
if fermi_error, ok := e.(FermiError); ok {
|
||||
fmt.Printf("Error: %s: %s\n", fermi_error.Code, fermi_error.Msg)
|
||||
fmt.Printf("Type \"help\" or \"h\" to see grammar and command flags\n")
|
||||
} else {
|
||||
fmt.Printf("Error: %s\n", e.Error())
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package main
|
||||
|
||||
type MyError struct {
|
||||
Code string
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (me MyError) Error() string {
|
||||
return me.Msg
|
||||
}
|
116
main/fermi.go
116
main/fermi.go
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"git.nunosempere.com/NunoSempere/fermi/sample"
|
||||
|
@ -84,7 +83,7 @@ const HELP_MSG = "1. Grammar:\n" +
|
|||
" + 1 10\n" +
|
||||
" * beta 1 10\n" +
|
||||
" 1 10 (multiplication taken as default operation)\n" +
|
||||
" =: x\n" +
|
||||
" =: x\n" +
|
||||
" .\n" +
|
||||
" 1 100\n" +
|
||||
" + x\n" +
|
||||
|
@ -94,6 +93,9 @@ const HELP_MSG = "1. Grammar:\n" +
|
|||
" 1 10\n" +
|
||||
" + beta 1 100\n" +
|
||||
" )\n" +
|
||||
" =. y\n" +
|
||||
" mx x 1 y 2.33\n" +
|
||||
" + mx x 30% y 70%\n" +
|
||||
" exit\n" +
|
||||
"\n" +
|
||||
"2. Command flags:\n" +
|
||||
|
@ -103,7 +105,7 @@ const HELP_MSG = "1. Grammar:\n" +
|
|||
" Specifies a file with a model to run\n" +
|
||||
" -n int\n" +
|
||||
" Specifies the number of samples to draw when using samples (default 100000)\n" +
|
||||
" -h Shows help message\n"
|
||||
" -h Shows help message"
|
||||
|
||||
const NORMAL90CONFIDENCE = 1.6448536269514727
|
||||
const INIT_DIST Scalar = Scalar(1)
|
||||
|
@ -126,16 +128,16 @@ func operateDistsAsSamples(dist1 Dist, dist2 Dist, op string) (Dist, error) {
|
|||
if ys[0] != 0 {
|
||||
zs[i] = xs[i] / ys[i]
|
||||
} else {
|
||||
fmt.Println("Error: When dividing as samples, division by zero")
|
||||
return nil, errors.New("Division by zero")
|
||||
// fmt.Println("Error: When dividing as samples, division by zero")
|
||||
return nil, FermiError{Code: "Division by zero", Msg: "When operating on samples, division by zero"}
|
||||
}
|
||||
case "+":
|
||||
zs[i] = xs[i] + ys[i]
|
||||
case "-":
|
||||
zs[i] = xs[i] - ys[i]
|
||||
default:
|
||||
fmt.Println("Error: Operation not recognized")
|
||||
return nil, errors.New("Operation not recognized")
|
||||
// fmt.Println("Error: Operation not recognized")
|
||||
return nil, FermiError{Code: "Unknown operation", Msg: "When operating on samples, operation not recognized"}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,14 +212,12 @@ func divideDists(old_dist Dist, new_dist Dist) (Dist, error) {
|
|||
switch n := new_dist.(type) {
|
||||
case Lognormal:
|
||||
if n.high == 0 || n.low == 0 {
|
||||
fmt.Println("Error: Can't divide by 0.0")
|
||||
return nil, errors.New("Error: division by zero")
|
||||
return nil, FermiError{Code: "Division by zero", Msg: "When operating two lognormals, one of the parameters is zero, which would result in division by zero"}
|
||||
}
|
||||
return multiplyLogDists(o, Lognormal{low: 1.0 / n.high, high: 1.0 / n.low}), nil
|
||||
case Scalar:
|
||||
if n == 0.0 {
|
||||
fmt.Println("Error: Can't divide by 0.0")
|
||||
return nil, errors.New("Error: division by zero scalar")
|
||||
return nil, FermiError{Code: "Division by zero", Msg: "When operating a lognormal with a scalar, trying to divide but the scalar is zero"}
|
||||
}
|
||||
return multiplyLogDistAndScalar(o, Scalar(1.0/n))
|
||||
}
|
||||
|
@ -229,8 +229,7 @@ func divideDists(old_dist Dist, new_dist Dist) (Dist, error) {
|
|||
return multiplyLogDistAndScalar(Lognormal{low: 1.0 / n.high, high: 1.0 / n.low}, o)
|
||||
case Scalar:
|
||||
if n == 0.0 {
|
||||
fmt.Println("Error: Can't divide by 0.0")
|
||||
return nil, errors.New("Error: division by zero scalar")
|
||||
return nil, FermiError{Code: "Division by zero", Msg: "When operating two scalars, trying to divide but the divisor is is zero"}
|
||||
}
|
||||
return Scalar(float64(o) / float64(n)), nil
|
||||
}
|
||||
|
@ -251,7 +250,7 @@ func operateDists(old_dist Dist, new_dist Dist, op string) (Dist, error) {
|
|||
case "-":
|
||||
return operateDistsAsSamples(old_dist, new_dist, "-")
|
||||
default:
|
||||
return nil, PrintAndReturnErr("Can't combine distributions in this way")
|
||||
return nil, FermiError{Code: "Unknown operation", Msg: "When operating distributions, operation not recognized"}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,9 +258,17 @@ func operateDists(old_dist Dist, new_dist Dist, op string) (Dist, error) {
|
|||
func parseMixture(words []string, vars map[string]Dist) (Dist, error) {
|
||||
// mx, mix, var weight var weight var weight ...
|
||||
// Check syntax
|
||||
if len(words)%2 != 0 {
|
||||
return nil, PrintAndReturnErr("Not a mixture. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n")
|
||||
switch {
|
||||
case len(words) < 1:
|
||||
return nil, FermiError{Code: "Not a mixture", Msg: "Input can't be a mixture, since it doesn't have enough words"}
|
||||
case words[0] != "mx":
|
||||
return nil, FermiError{Code: "Not a mixture", Msg: "Input can't be a mixture, since it is not preceded by the mx keyword"}
|
||||
case len(words)%2 != 1:
|
||||
return nil, FermiError{Code: "Not a mixture", Msg: "When parsing a mixture, input doesn't have equal number of variables and weights. \nMixture syntax: \nmx x 20% y 70% z 10%\ni.e.: mx var weight var2 weight2 ... var_n weight_n"}
|
||||
case len(words) < 5:
|
||||
return nil, FermiError{Code: "Not a mixture", Msg: "When parsing a mixture, not enough words. \nMixture syntax: \nmx x 20% y 70% z 10%\ni.e.: mx var weight var2 weight2 ... var_n weight_n"}
|
||||
}
|
||||
words = words[1:] // crop "mx" at the beginning
|
||||
|
||||
var fs []func(int, sample.State) float64
|
||||
var weights []float64
|
||||
|
@ -270,14 +277,14 @@ func parseMixture(words []string, vars map[string]Dist) (Dist, error) {
|
|||
if i%2 == 0 {
|
||||
dist, exists := vars[word]
|
||||
if !exists {
|
||||
return nil, PrintAndReturnErr("Expected mixture variable but didn't get a variable. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n")
|
||||
return nil, FermiError{Code: "Not a mixture variable", Msg: "When parsing a mixture, expected mixture variable but didn't get a variable. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n"}
|
||||
}
|
||||
f := dist.Sampler
|
||||
fs = append(fs, f)
|
||||
} else {
|
||||
weight, err := ParseFloat(word)
|
||||
if err != nil {
|
||||
return nil, PrintAndReturnErr("Expected mixture weight but didn't get a float. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n")
|
||||
return nil, FermiError{Code: "Not a mixture weight", Msg: "When parsing a mixture, expected mixture weight but didn't get a variable. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n"}
|
||||
}
|
||||
weights = append(weights, weight)
|
||||
}
|
||||
|
@ -291,26 +298,26 @@ func parseMixture(words []string, vars map[string]Dist) (Dist, error) {
|
|||
}
|
||||
|
||||
/* Parser and repl */
|
||||
func parseWordsErr(err_msg string) (string, Dist, error) {
|
||||
return "", nil, PrintAndReturnErr(err_msg)
|
||||
}
|
||||
func parseWordsIntoOpAndDist(words []string, vars map[string]Dist) (string, Dist, error) {
|
||||
parseWordsErr := func(msg string) (string, Dist, error) {
|
||||
return "", nil, FermiError{Code: "Malformed input", Msg: "When parsing words into operator and distribution: " + msg}
|
||||
}
|
||||
|
||||
op := ""
|
||||
var dist Dist
|
||||
|
||||
switch words[0] {
|
||||
case "*", "/", "+", "-", "mx":
|
||||
case "*", "/", "+", "-":
|
||||
op = words[0]
|
||||
words = words[1:]
|
||||
default:
|
||||
op = "*"
|
||||
}
|
||||
|
||||
switch len(words) {
|
||||
case 0:
|
||||
switch {
|
||||
case len(words) == 0:
|
||||
return parseWordsErr("Operator must have operand; can't operate on nothing")
|
||||
case 1:
|
||||
case len(words) == 1:
|
||||
var_word, var_word_exists := vars[words[0]]
|
||||
single_float, err1 := ParseFloat(words[0]) // abstract this away to search for K/M/B/T/etc.
|
||||
switch {
|
||||
|
@ -321,7 +328,7 @@ func parseWordsIntoOpAndDist(words []string, vars map[string]Dist) (string, Dist
|
|||
case err1 != nil && !var_word_exists:
|
||||
return parseWordsErr("Trying to operate on a scalar, but scalar is neither a float nor an assigned variable")
|
||||
}
|
||||
case 2:
|
||||
case len(words) == 2:
|
||||
new_low, err1 := ParseFloat(words[0])
|
||||
new_high, err2 := ParseFloat(words[1])
|
||||
switch {
|
||||
|
@ -335,7 +342,7 @@ func parseWordsIntoOpAndDist(words []string, vars map[string]Dist) (string, Dist
|
|||
return parseWordsErr("Trying to parse two floats as a lognormal, but the first number is larger than the second number")
|
||||
}
|
||||
dist = Lognormal{low: new_low, high: new_high}
|
||||
case 3:
|
||||
case len(words) == 3:
|
||||
switch {
|
||||
case words[0] == "beta" || words[0] == "b":
|
||||
a, err1 := ParseFloat(words[1])
|
||||
|
@ -347,18 +354,12 @@ func parseWordsIntoOpAndDist(words []string, vars map[string]Dist) (string, Dist
|
|||
default:
|
||||
return parseWordsErr("Input not understood or not implemented yet")
|
||||
}
|
||||
default:
|
||||
switch op {
|
||||
case "mx":
|
||||
tmp, err := parseMixture(words, vars)
|
||||
if err != nil {
|
||||
return parseWordsErr("Error parsing a mixture: " + err.Error())
|
||||
}
|
||||
dist = tmp
|
||||
op = "*"
|
||||
default:
|
||||
return parseWordsErr("Input not understood or not implemented yet")
|
||||
case len(words) >= 4: // four or more words
|
||||
if words[0] == "mx" {
|
||||
dist, err := parseMixture(words, vars)
|
||||
return op, dist, err
|
||||
}
|
||||
return parseWordsErr("Input not understood or not implemented yet")
|
||||
}
|
||||
return op, dist, nil
|
||||
}
|
||||
|
@ -379,17 +380,11 @@ replForLoop:
|
|||
words := strings.Split(new_line_trimmed, " ")
|
||||
|
||||
switch {
|
||||
case strings.TrimSpace(new_line_trimmed) == "": /* Empty line case */
|
||||
|
||||
/* Empty line */
|
||||
case strings.TrimSpace(new_line_trimmed) == "":
|
||||
continue replForLoop
|
||||
/* Parenthesis */
|
||||
case len(words) == 2 && (words[0] == "*" || words[0] == "+" || words[0] == "-" || words[0] == "/") && words[1] == "(":
|
||||
new_stack := runRepl(Stack{old_dist: INIT_DIST, vars: stack.vars}, reader, echo_flag)
|
||||
combined_dist, err := operateDists(stack.old_dist, new_stack.old_dist, words[0])
|
||||
if err == nil {
|
||||
stack.old_dist = combined_dist
|
||||
}
|
||||
case len(words) == 1 && words[0] == ")":
|
||||
return stack
|
||||
|
||||
/* Special operations */
|
||||
case words[0] == "exit" || words[0] == "e":
|
||||
os.Exit(0)
|
||||
|
@ -402,6 +397,7 @@ replForLoop:
|
|||
fmt.Println()
|
||||
case words[0] == "stats" || words[0] == "s":
|
||||
PrettyPrintStats(stack.old_dist)
|
||||
|
||||
/* Variable assignment */
|
||||
case words[0] == "=:" && len(words) == 2:
|
||||
stack.vars[words[1]] = stack.old_dist
|
||||
|
@ -411,16 +407,36 @@ replForLoop:
|
|||
fmt.Printf("%s ", words[1])
|
||||
PrettyPrintDist(stack.old_dist)
|
||||
stack.old_dist = INIT_DIST
|
||||
|
||||
/* Parenthesis */
|
||||
case len(words) == 2 && (words[0] == "*" || words[0] == "+" || words[0] == "-" || words[0] == "/") && words[1] == "(":
|
||||
new_stack := runRepl(Stack{old_dist: INIT_DIST, vars: stack.vars}, reader, echo_flag)
|
||||
combined_dist, err := operateDists(stack.old_dist, new_stack.old_dist, words[0])
|
||||
if err != nil {
|
||||
PrintError(err)
|
||||
} else {
|
||||
stack.old_dist = combined_dist
|
||||
}
|
||||
case len(words) == 1 && words[0] == ")":
|
||||
return stack
|
||||
|
||||
/* Bread and butter distribution operations */
|
||||
default:
|
||||
op, new_dist, err := parseWordsIntoOpAndDist(words, stack.vars)
|
||||
if err != nil {
|
||||
PrintError(err)
|
||||
PrettyPrintDist(stack.old_dist)
|
||||
continue replForLoop
|
||||
}
|
||||
combined_dist, err := operateDists(stack.old_dist, new_dist, op)
|
||||
if err == nil {
|
||||
stack.old_dist = combined_dist
|
||||
if err != nil {
|
||||
PrintError(err)
|
||||
PrettyPrintDist(stack.old_dist)
|
||||
continue replForLoop
|
||||
}
|
||||
stack.old_dist = combined_dist
|
||||
}
|
||||
|
||||
PrettyPrintDist(stack.old_dist)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user