// This library gives hints to help you through a calculation. // These hints are mostly based on failed calculations that I've seen // performed through the web-based interface. use HTMLUtils.frink // Str should include the whole input program like "foot -> m" getHints[str, fromStr, toStr, results] := { h = "" c = str // Using "per" for division which is ambiguous. For example, when you // say "miles per hour" you want miles/hour, but when you say // "days per week" you want to see 7, which is actually "week/day" // Best not to let this ambiguous English bit slip in. if c =~ %r/\bper\b/i { c =~ %s/\bper\b/\//ig h = h + """
• The word `per` is currently not allowed as a synonym for division (because it's ambiguous in some cases.)

For example, when you say "miles per hour" you want miles/hour, but when you say "days per week" you probably expect to get 7, which is actually "week/day" (the size of a week divided by the size of a day.) Since this is ambiguous in English, I don't try to guess what you mean.

Please use the `/` character to indicate division.

`""" + HTMLEncode[c] + "`\n" } // Frink uses square brackets for function calls, not parentheses. // This is to preserve the implicit multiplication in standard mathematical // notation like x(x+1) if c =~ %r/([a-zA-Z][a-zA-Z]*)\((.*?)\)/ // Function calls with parens { c =~ %s/([a-zA-Z][a-zA-Z]*)\((.*?)\)/\$1[\$2]/g h = h + """

• Frink uses square brackets `[ ]` to indicate function calls, and parentheses to indicate grouping. Why? For example,

`sin[30 degrees]`

If you intended to call a function, you might try to write it as something like:

`""" + HTMLEncode[c] + "`\n" } // Ounces are mass. Fluid ounces are volume. if c =~ %r/\b(oz|ounces?|fluid)\b/ // oz / floz confusion { c =~ %s/\b(fluid\s+(ounces?\b|(oz\.|oz\b)))/floz/gi c =~ %s/\b(fl\.?\s+(ounces?\b|(oz\.|oz\b)))/floz/gi c =~ %s/\b((oz\.|oz\b)|ounces?\b|fluid\b)/floz/gi h = h + """

• The wonderful English system of measurements uses the word `ounce` to describe two completely different units of measure. One is a unit of mass, the other a unit of volume. When referring to volume, the term is properly called "fluid ounce." In Frink, please use `fluidounce` or `floz` to indicate the fluid ounce and `ounce` or `oz` to refer to mass. If you meant the fluid ounce, your calculation becomes:

`""" + HTMLEncode[c] + "`\n" } // Warn about the word "of." Frink doesn't try to parse sentences, // because they're usually ambiguous. if c =~ %r/\bof\b/i { [property, noun] = c =~ %r/(\w+)\s+of\s+(?:(?:a|an|the)\s+)?(\w+)/i property = lc[property] noun = lc[noun] fallbackName = "\$noun\$property" fallback = unit[fallbackName] fallbackHelp = "" if (fallback != undef) { c =~ %s/(\w+)\s+of\s+(?:(?:a|an|the)\s+)?(\w+)/\$2 + \$1/egi fallbackHelp = """

In fact, Frink has a unit called `\$fallbackName` which is equal to:

\$fallbackName = """ + HTMLEncode["\$fallback"] + """\n

If that's what you need, your calculation might look something like:

```""" + HTMLEncode[c] + "```\n"; } else c =~ %s/\s+of\s+(?:(?:a|an|the)\s+)?/ /gi h = h + """

• Frink currently doesn't use the word "of" as a modifier. All Frink symbols are a single word. If you're looking for something like `\$property of \$noun`, it might be called something like: `\$noun\$property` or `\${noun}_\$property`. \$fallbackHelp

If you want to see all of the units that contain "\$noun", you should type part or all of "\$noun" into the "Lookup" field or, better yet, enter `??\$noun` into the From: box and see what you get. More about integrated help.\n""" } // "the" is not used. Sentences are ambiguous. if [useless] = c =~ %r/\bthe\b/i { c =~ %s/\bthe\b//ig h = h + """

• The word "the" has no meaning in Frink. Please eliminate it. Your calculation might become:

```""" + HTMLEncode[c] + "```\n" } // The Google calculator uses the word "in" where Frink uses -> if c =~ %r/\bin\b/i { c =~ %s/\bin\b/->/i h = h + """

• Instead of the word "in", Frink uses the `->` operator to convert to different units. Your calculation might become:

```""" + HTMLEncode[c] + "```\n" + """

However, it's unnecessary to type this symbol. Just enter the units you're converting from in the "From:" box and the units you're converting to in the "To:" box above.\n (Hint: You can probably switch between these quickly using the tab key.)""" } // I sometimes see "kilo/kilos/k" used by itself. A thousand what? if [let] = c =~ %r/\b(kilos?|k)\b/ { h = h + """

• If you're using "k" or "kilo" or "kilos" to mean 1000 of something, you need to specify what you're specifying 1000 of, or it's ambiguous. Did you mean "kilograms" or "kg" or maybe "kilometers" or "km"?""" if (let == "k") h = h + """

If you meant the temperature scale kelvin, that's either written "kelvin" (with a small k) or simply "K" with a capital K.""" } // Temperature conversions. Fahrenheit and Celsius, apart from being // almost always misspelled, cannot be treated as normal multiplicative // units because they don't have a reasonable zero-point at absolute // zero. Point the user to the different meanings of the units. if c =~ %r/\b(deg|degC|degF|F|C|(?:degree|deg)?[cC]el[sc]ius|(?:degree|deg)?[fF]ah?renheit|rankine|Rankine|Kelvin)\b/ { h = h + """

• Are you doing a temperature calculation? Keep in mind that if you're using the Celsius or Fahrenheit system, the zero point is not at absolute zero, so these units can't be written as normal multiplicative units. You also need to be clear whether you're specifying an absolute temperature or the size of a degree in the Fahrenheit or Celsius system. (You use the size only when you're indicating the difference between two temperatures.)

Frink can do it all, but you need to be unambiguous as to which you mean, so you don't get a wrong answer. For more information, please see the Temperature Scales section of the documentation.""" if c =~ %r/\b[cC]elcius\b/ h = h + """

By the way, you spelled "Celsius" wrong. I see that a lot.

""" if c =~ %r/\b[fF]arenheit\b/ h = h + """

By the way, you spelled "Fahrenheit" wrong. I see that a lot.

""" } // Look for attempted date calculations without surrounding pound // signs. if c =~ %r/\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/ if ! (c =~ %r/#/) { h = h + """
• Are you trying to do a date calculation? To be unambiguous, you need to surround dates with pound signs something like

`# August 19, 1969 #`

Frink recognizes lots of date/time formats, and can do a lot of calculations and conversions with dates. Please see the Date/Time Handling section of the documentation for more information.""" } // Provide suggestions for possible unparsed dates. if results =~ %r/#/ { h = h + """

• Are you trying to do a date calculation? To be unambiguous, you need to surround dates with pound signs something like

`# August 19, 1969 #`. If the date you're entering is not being parsed, please use one of the formats listed in the official formats. I know you've learned from the Y2K fiasco that you must enter 4-digit years. (Most-significant digits first are recommended.) See the Date/Time Handling section of the documentation for more information.""" } // Warn about standard mathematical precedence in things like // 30 miles / 2 hours where the calculation may actually want to be: // 30 miles / (2 hours) if [quot] = c =~ %r/\/\s*(\d+(?:\.\d+)?\s*[[:alpha:]]\w*)/ { c =~ %s/\/\s*(\d+(?:\.\d+)?\s*\w+)/\/ (\$1) /g h = h + """

• There is an implicit multiplication in "\$quot". Frink follows normal mathematical rules of precedence (multiplication and division are at the same precedence, and performed left to right,) so you may need to put parentheses around the right-hand-side of the equation:

`""" + HTMLEncode[c] + """`

See this section of the FAQ for more information.""" } // People use the date calculations from the manual and miss the // obvious and necessary brackets after the now[] function. if c =~ %r/\bnow\b\s*[^\[]/ { c =~ %s/\bnow\b/now\[\]/g h = h + """

• Did you mean the function `now[]` ? That's a function, so you need the square brackets after it. Your calculation might become:

`""" + HTMLEncode[c] + "`" } // Check for capitalization for [word] results =~ %r/(\w+)\s*\(undefined symbol\)/ig { printed = false if (word =~ %r/^(of|the|per|a|an)\$/i) or (length[word] <= 1) next array = array[select[units[], regex["^\$word\$", "i"]]] len = length[array] for cap = array { if ((cap == word) and (len == 1)) next if (not printed) { h = h + "

• Frink is case-sensitive. Did you mean one of the following capitalizations for `\$word`?\n
" printed = true } h = h + "
• `\$cap` = " + unit[cap] + "\n" if len == 1 { rep = subst["\\b\$word\\b", cap, "g"] c =~ rep h = h + """

`""" + HTMLEncode[c] + "`

" } } if printed h = h + "
" else { // If an exact capitalization match isn't found, // see if the word occurs as a substring in a unit name. altstr = "" // Skip certain words. if (word =~ %r/^(of|the|per|a|an)\$/i) or (length[word] <= 1) next // Remove plural ending if the word is >= 4 characters long if ((word =~ %r/s\$/i) and (length[word] >= 4)) singular = substrLen[word, 0, length[word]-1] else singular = word // If word is 3 chars or shorter, just look for it at the // beginning or the end. if (length[singular] <= 3) pat = regex["((^\$singular)|(\$singular\$))", "i"] else pat = regex["\$singular", "i"] x = sort[array[select[units[], pat]]] y = new array // Look for fuzzy spelling len = length[word] lowerword = lowercase[word] for poss = units[] { lposs = length[poss] maxlen = max[len, lposs] if (abs[lposs - len] / maxlen <= 1/3) { closeness = editDistanceDamerau[lowerword, lowercase[poss]] if (closeness/maxlen <= 1/3) y.push[ [poss, closeness] ] } } // Sort by closeness sort[y, byColumn] for [poss, closeness] y x.push[poss] for option = x { if word == option // Avoid exact matches. next if (altstr == "") altstr = """
• The symbol `\$word` was not found. Perhaps you meant one of the following units:\n
""" unit = unit[option] if unit conforms currency // Don't fetch currencies altstr = altstr + "
• `\$option` (currency)\n" else altstr = altstr + "
• `\$option` = \$unit\n" } if (altstr != "") { h = h + altstr + "\n
" altstr = "" } } } // Note about bad capitalizations of SI prefix names like "kilo" if [word] = results =~ %r/(\w+)\s*\(undefined symbol\)/i { if [prefix, rest] = word =~ %r/^(yotta|zetta|exa|peta|tera|giga|mega|kilo|hecto|deka|deci|centi|milli|micro|nano|pico|femto|atto|zepto|yocto)(.*)/i { if (prefix =~ %r/[A-Z]/) // And it contains a capital letter { pRight = lc[prefix] sub = subst[word, "\$pRight\$rest"] c =~ sub h = h + """
• You appear to be miscapitalizing the SI prefix "`\$pRight`". Under the rules of the International System of Units, these names must not be capitalized. See the official list of SI prefixes for more details. Your calculation might become:

`""" + HTMLEncode[c] + """`

(You may also want to verify the correct capitalization of SI units for "`\$rest`" if you continue to have problems.)""" } } else // Try fixing bad "K" prefix if [prefix, rest] = word =~ %r/^(K)(.+)/ { pRight = lc[prefix] sub = subst[word, "\$pRight\$rest"] c =~ sub h = h + """

• You may be miscapitalizing the SI prefix "`\$pRight`". Under the rules of the International System of Units, all prefix symbols have only one correct capitalization. See the official list of SI prefixes for more details. Your calculation might become:

`""" + HTMLEncode[c] + """`

(You may also want to verify the correct capitalization of SI units for "`\$rest`" if you continue to have problems.)""" } } // Note about undefined symbol if [word] = results =~ %r/(\w+)\s*\(undefined symbol\)/i { // Remove plural ending if the word is >= 4 characters long if ((word =~ %r/s\$/i) and (length[word] >= 4)) word = substrLen[word, 0, length[word]-1] h = h + """

• Your calculation contained one or more names that Frink does not know about. Generally, if you don't know the name and/or capitalization that Frink uses for a particular unit, type one or two question marks and then all or part of the singular form of the name you're looking for. (Two question marks gives more information. Typing just part of the name may help.) For example:

`??\$word`

• Frink uses the letter `h` to stand for Planck's constant. If you mean `hour`, please use `hour` or `hr`:
```""" + HTMLEncode[c] + "```\n" } if h == "" return h else return """