diff --git a/index.html b/index.html index c3dfe07..7aef48a 100644 --- a/index.html +++ b/index.html @@ -18,13 +18,7 @@ // Load KeyboardEvent polyfill for old browsers keyboardeventKeyPolyfill.polyfill(); - var globalEnv = Insect.initialEnvironment; - - var commands = ["help", "list", "ls", "reset", "clear", "quit", "exit"]; - - var functions = ["acos", "acosh", "asin", "asinh", "atan", "atanh", - "ceil", "cos", "cosh", "exp", "floor", "ln", "log", - "log10", "round", "sin", "sinh", "sqrt", "tan", "tanh"]; + var insectEnv = Insect.initialEnvironment; function interpret(line) { // Skip empty lines or line comments @@ -34,8 +28,8 @@ } // Run insect - var res = Insect.repl(Insect.fmtJqueryTerminal)(globalEnv)(line); - globalEnv = res.newEnv; + var res = Insect.repl(Insect.fmtJqueryTerminal)(insectEnv)(line); + insectEnv = res.newEnv; // Handle shell commands if (res.msgType == "clear") { @@ -45,7 +39,7 @@ } else if (res.msgType == "quit") { // Treat as reset: this.clear(); - globalEnv = Insect.initialEnvironment; + insectEnv = Insect.initialEnvironment; return ""; } @@ -83,10 +77,14 @@ return line.trim() !== ""; }, completion: function(inp, cb) { - var variables = Object.keys(globalEnv); - cb(commands.concat(functions) + var variables = Object.keys(insectEnv); + + var keywords = + variables.concat(Insect.functions) .concat(Insect.supportedUnits) - .concat(variables)); + .concat(Insect.commands); + + cb(keywords.sort()); } }); }); diff --git a/index.js b/index.js index df9dfa7..ca37f7d 100755 --- a/index.js +++ b/index.js @@ -52,7 +52,26 @@ if (interactive) { var rl = readline.createInterface({ input: process.stdin, output: process.stdout, - prompt: '\x1b[01m>>>\x1b[0m ' + prompt: '\x1b[01m>>>\x1b[0m ', + completer: function(line) { + var variables = Object.keys(insectEnv); + + var keywords = + variables.concat(Insect.functions) + .concat(Insect.supportedUnits) + .concat(Insect.commands); + + var lastWord = line; + if (line.trim() !== "") { + var words = line.split(/\b/); + lastWord = words[words.length - 1]; + keywords= keywords.filter(function(kw) { + return kw.indexOf(lastWord) === 0; + }); + } + + return [keywords, lastWord]; + } }); rl.prompt(); diff --git a/src/Insect.purs b/src/Insect.purs index ba493dc..0c9e653 100644 --- a/src/Insect.purs +++ b/src/Insect.purs @@ -5,6 +5,8 @@ module Insect , fmtPlain , fmtJqueryTerminal , fmtConsole + , commands + , functions ) where import Prelude @@ -18,6 +20,7 @@ import Text.Parsing.Parser (parseErrorPosition, parseErrorMessage) import Insect.Parser (Dictionary(..), (==>), normalUnitDict, imperialUnitDict, parseInsect) +import Insect.Parser as P import Insect.Interpreter (MessageType(..), Message(..), runInsect) import Insect.Environment (Environment) import Insect.Environment as E @@ -87,3 +90,11 @@ fmtJqueryTerminal = F.fmtJqueryTerminal -- | Re-export the console formatter fmtConsole ∷ Formatter fmtConsole = F.fmtConsole + +-- | Re-export the list of commands +commands ∷ Array String +commands = P.commands + +-- | Re-export the list of function names +functions ∷ Array String +functions = P.functions diff --git a/src/Insect/Parser.purs b/src/Insect/Parser.purs index 0bff345..ff28d3c 100644 --- a/src/Insect/Parser.purs +++ b/src/Insect/Parser.purs @@ -3,6 +3,8 @@ module Insect.Parser ( DictEntry(..) , (==>) , Dictionary(..) + , commands + , functions , siPrefixDict , normalUnitDict , imperialUnitDict @@ -26,7 +28,7 @@ import Quantities (DerivedUnit, atto, bit, byte, centi, day, deci, degree, exa, import Data.Array (some, fromFoldable) import Data.Either (Either(..)) import Data.Decimal (Decimal, fromString, fromNumber, isFinite) -import Data.Foldable (foldr) +import Data.Foldable (foldr, foldMap) import Data.Foldable as F import Data.List (List, many, init, last) import Data.Maybe (Maybe(..), fromMaybe) @@ -55,6 +57,10 @@ identStart = letter <|> char '_' identLetter ∷ P Char identLetter = letter <|> digit <|> char '_' <|> char '\'' +-- | A list of allowed commands +commands ∷ Array String +commands = ["help", "?", "list", "ls", "ll", "reset", "clear", "cls", "quit", "exit"] + -- | The language definition. insectLanguage ∷ LanguageDef insectLanguage = LanguageDef @@ -66,8 +72,7 @@ insectLanguage = LanguageDef , identLetter: identLetter , opStart: oneOf ['+', '-', '*', '·', '⋅', '×', '/', '÷', '^', '='] , opLetter: oneOf ['>', '*'] - , reservedNames: ["help", "?", "list", "ls", "reset", "clear", "cls", "quit", - "exit", "²", "³"] + , reservedNames: commands <> ["²", "³"] , reservedOpNames: ["->", "+", "-", "*", "·", "⋅", "×", "/", "÷", "^", "**", "="] , caseSensitive: true @@ -269,29 +274,39 @@ derivedUnit = variable ∷ P Expression variable = Variable <$> token.identifier +-- | Possible names for the mathematical functions. +funcNameDict ∷ Dictionary Func +funcNameDict = Dictionary + [ Acosh ==> ["acosh"] + , Acos ==> ["acos"] + , Asinh ==> ["asinh"] + , Asin ==> ["asin"] + , Atanh ==> ["atanh"] + , Atan ==> ["atan"] + , Ceil ==> ["ceil"] + , Cosh ==> ["cosh"] + , Cos ==> ["cos"] + , Exp ==> ["exp"] + , Floor ==> ["floor"] + , Log10 ==> ["log10"] + , Ln ==> ["log", "ln"] + , Round ==> ["round"] + , Sinh ==> ["sinh"] + , Sin ==> ["sin"] + , Sqrt ==> ["sqrt"] + , Tanh ==> ["tanh"] + , Tan ==> ["tan"] + ] + +-- | A list of all mathematical function names (for tab-completion). +functions ∷ Array String +functions = + case funcNameDict of + Dictionary dict → foldMap (\(_ ==> names) → names) dict + -- | Parse the name of a mathematical function. funcName ∷ P Func -funcName = - (string "acosh" *> pure Acosh) - <|> (string "acos" *> pure Acos) - <|> (string "asinh" *> pure Asinh) - <|> (string "asin" *> pure Asin) - <|> (string "atanh" *> pure Atanh) - <|> (string "atan" *> pure Atan) - <|> (string "ceil" *> pure Ceil) - <|> (string "cosh" *> pure Cosh) - <|> (string "cos" *> pure Cos) - <|> (string "exp" *> pure Exp) - <|> (string "floor" *> pure Floor) - <|> (string "log10" *> pure Log10) - <|> (string "log" *> pure Ln) - <|> (string "ln" *> pure Ln) - <|> (string "round" *> pure Round) - <|> (string "sinh" *> pure Sinh) - <|> (string "sin" *> pure Sin) - <|> (string "sqrt" *> pure Sqrt) - <|> (string "tanh" *> pure Tanh) - <|> (string "tan" *> pure Tan) +funcName = buildDictParser funcNameDict "function name" -- | A version of `sepBy1` that returns a `NonEmpty List`. sepBy1 ∷ ∀ m s a sep. Monad m ⇒ ParserT s m a → ParserT s m sep → ParserT s m (NonEmpty List a) @@ -390,7 +405,7 @@ command ∷ P Command command = ( (reserved "help" <|> reserved "?") *> pure Help - <|> (reserved "list" <|> reserved "ls") *> pure List + <|> (reserved "list" <|> reserved "ls" <|> reserved "ll") *> pure List <|> (reserved "reset") *> pure Reset <|> (reserved "clear" <|> reserved "cls") *> pure Clear <|> (reserved "quit" <|> reserved "exit") *> pure Quit diff --git a/test/Main.purs b/test/Main.purs index 8ee5027..c9112b2 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -581,7 +581,7 @@ main = runTest do expectOutput myEnv "10m" "2 x" expectOutput myEnv "25m²" "x x" expectOutput myEnv "25m²" "x²" - expectOutput myEnv "Unknown variable 'x2'" "x2" + expectOutput myEnv "Unknown identifier: x2" "x2" test "Function inverses" do expectOutput' "0.1234" "sin(asin(0.1234))"