{-# LANGUAGE OverloadedStrings #-} module Main where import Test.Hspec import Data.HashMap.Strict import Data.Maybe (fromJust) import Network.URI import Data.Scientific (toRealFloat) import Data.CSS.Syntax.Tokens import Data.CSS.Syntax.StyleSheet (parse, StyleSheet(..), TrivialStyleSheet(..), scanAtRule, scanValue) import Data.CSS.Syntax.Selector import Data.CSS.Style.Common import Data.CSS.Style.Selector.Index import Data.CSS.Style.Selector.Interpret import Data.CSS.Style import Data.CSS.StyleTree import Data.CSS.Preprocessor.Conditions import Data.CSS.Preprocessor.Conditions.Expr (Datum(..), Op(..), parse', eval) import qualified Data.CSS.Preprocessor.Text as Txt main :: IO () main = hspec spec spec :: Spec spec = do describe "Canary" $ do it "Test framework works" $ do True `shouldBe` True describe "Parsing" $ do it "Can scan @rules" $ do scanAtRule [Ident "utf-8", Semicolon, Ident "a"] `shouldBe` ([Ident "utf-8", Semicolon], [Ident "a"]) scanAtRule [Ident "before", LeftCurlyBracket, Ident "inside", RightCurlyBracket, Ident "after"] `shouldBe` ( [Ident "before", LeftCurlyBracket, Ident "inside", RightCurlyBracket], [Ident "after"]) it "Ignores @rules" $ do parse emptyStyle "@encoding 'utf-8';" `shouldBe` emptyStyle parse emptyStyle " @encoding 'utf-8';" `shouldBe` emptyStyle parse emptyStyle "@encoding 'utf-8'; " `shouldBe` emptyStyle parse emptyStyle "@media print { a:link {color: green;} }" `shouldBe` emptyStyle parse emptyStyle " @media print { a:link {color: green;} }" `shouldBe` emptyStyle parse emptyStyle "@media print { a:link {color: green;} } " `shouldBe` emptyStyle parse emptyStyle "@encoding 'utf-8'; a {color:green}" `shouldBe` linkStyle parse emptyStyle "a {color:green}@encoding 'utf-8';" `shouldBe` linkStyle parse emptyStyle "@media print{a{color:black;}}a {color:green}" `shouldBe` linkStyle parse emptyStyle "a {color:green} @media print {a{color:black;}}" `shouldBe` linkStyle it "Parses style rules" $ do -- Syntax examples from "Head First HTML & CSS with XHTML" parse emptyStyle "bedroom { drapes: blue; carpet: wool shag; }" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Tag "bedroom"]) [ ("drapes", [Ident "blue"]), ("carpet", [Ident "wool", Ident "shag"]) ] ""] parse emptyStyle " bathroom{tile :1in white;drapes :pink}" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Tag "bathroom"]) [ ("tile", [Dimension "1" (NVInteger 1) "in", Ident "white"]), ("drapes", [Ident "pink"]) ] ""] it "Parses selectors" $ do parse emptyStyle ".class {}" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Class "class"]) [] "" ] parse emptyStyle "*.class {}" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Class "class"]) [] "" ] parse emptyStyle "#id {}" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Id "id"]) [] "" ] parse emptyStyle "[attr] {}" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Property Nothing "attr" Exists]) [] "" ] parse emptyStyle "a , b {}" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Tag "b"]) [] "", StyleRule (Element [Tag "a"]) [] "" ] parse emptyStyle "a b {}" `shouldBe` TrivialStyleSheet [ StyleRule (Descendant (Element [Tag "a"]) [Tag "b"]) [] "" ] parse emptyStyle "a > b {}" `shouldBe` TrivialStyleSheet [ StyleRule (Child (Element [Tag "a"]) [Tag "b"]) [] "" ] parse emptyStyle "a ~ b {}" `shouldBe` TrivialStyleSheet [ StyleRule (Sibling (Element [Tag "a"]) [Tag "b"]) [] "" ] parse emptyStyle "a + b {}" `shouldBe` TrivialStyleSheet [ StyleRule (Adjacent (Element [Tag "a"]) [Tag "b"]) [] "" ] parse emptyStyle "a::before {}" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Tag "a"]) [] "before" ] parse emptyStyle "a:before {}" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Tag "a", Psuedoclass "before" []]) [] "" ] describe "Style Index" $ do it "Retrieves appropriate styles" $ do let index = addStyleRule styleIndex 0 $ styleRule' sampleRule let element = ElementNode { name = "a", namespace = "", parent = Nothing, previous = Nothing, attributes = [ Attribute "class" "" "external", Attribute "href" "" "https://adrian.geek.nz/", Attribute "id" "" "mysite" ] } let element2 = ElementNode { name = "b", namespace = "", parent = Just element, previous = Just element, -- Invalid tree, oh well. attributes = [] } rulesForElement index element `shouldBe` [sampleRule] rulesForElement index element2 `shouldBe` [] let rule1 = StyleRule (Element [Class "external"]) [("color", [Ident "green"])] "" let index1 = addStyleRule styleIndex 0 $ styleRule' rule1 rulesForElement index1 element `shouldBe` [rule1] rulesForElement index1 element2 `shouldBe` [] let rule2 = StyleRule (Element [Id "mysite"]) [("color", [Ident "green"])] "" let index2 = addStyleRule styleIndex 0 $ styleRule' rule2 rulesForElement index2 element `shouldBe` [rule2] rulesForElement index2 element2 `shouldBe` [] let rule3 = StyleRule (Element [Property Nothing "href" $ Prefix "https://"]) [("color", [Ident "green"])] "" let index3 = addStyleRule styleIndex 0 $ styleRule' rule3 rulesForElement index3 element `shouldBe` [rule3] rulesForElement index3 element2 `shouldBe` [] describe "Selector Compiler" $ do it "Correctly evaluates selectors" $ do let parentEl = ElementNode { name = "a", namespace = "", parent = Nothing, previous = Nothing, attributes = [ Attribute "class" "" "external secure link", Attribute "href" "" "https://adrian.geek.nz/index.html", Attribute "id" "" "mysite", Attribute "lang" "" "en-US" ] } let sibling = ElementNode { name = "img", namespace = "", parent = Just parentEl, previous = Nothing, attributes = [] } let child = ElementNode { name = "b", namespace = "", parent = Just parentEl, previous = Just sibling, attributes = [] } let selector1 = compile (Element [Tag "a"]) selector1 parentEl `shouldBe` True selector1 sibling `shouldBe` False selector1 child `shouldBe` False let selector2 = compile (Element [Class "external"]) selector2 parentEl `shouldBe` True selector2 sibling `shouldBe` False selector2 child `shouldBe` False let selector3 = compile (Element [Id "mysite"]) selector3 parentEl `shouldBe` True selector3 sibling `shouldBe` False selector3 child `shouldBe` False let selector4 = compile (Element [Property Nothing "lang" Exists]) selector4 parentEl `shouldBe` True selector4 sibling `shouldBe` False selector4 child `shouldBe` False let selector5 = compile (Element [Property Nothing "class" $ Include "secure"]) selector5 parentEl `shouldBe` True selector5 sibling `shouldBe` False selector5 child `shouldBe` False let selector6 = compile (Element [Property Nothing "href" $ Prefix "https://"]) selector6 parentEl `shouldBe` True selector6 sibling `shouldBe` False selector6 child `shouldBe` False let selector7 = compile (Element [Property Nothing "href" $ Suffix ".html"]) selector7 parentEl `shouldBe` True selector7 sibling `shouldBe` False selector7 child `shouldBe` False let selector8 = compile (Element [Property Nothing "href" $ Substring ".geek.nz"]) selector8 parentEl `shouldBe` True selector8 sibling `shouldBe` False selector8 child `shouldBe` False let selector9 = compile (Element [Property Nothing "lang" $ Dash "en"]) selector9 parentEl `shouldBe` True selector9 sibling `shouldBe` False selector9 child `shouldBe` False let selectorA = compile (Element [Property Nothing "lang" $ Dash "en-US"]) selectorA parentEl `shouldBe` True selectorA sibling `shouldBe` False selectorA child `shouldBe` False let selectorB = compile (Element [Property Nothing "lang" $ Dash "en-UK"]) selectorB parentEl `shouldBe` False selectorB sibling `shouldBe` False selectorB child `shouldBe` False -- TODO These could be tested better. let selectorC = compile $ Child (Element [Tag "a"]) [Tag "b"] selectorC parentEl `shouldBe` False selectorC sibling `shouldBe` False selectorC child `shouldBe` True let selectorD = compile $ Descendant (Element [Tag "a"]) [Tag "b"] selectorD parentEl `shouldBe` False selectorD sibling `shouldBe` False selectorD child `shouldBe` True let selectorE = compile $ Sibling (Element [Tag "img"]) [Tag "b"] selectorE parentEl `shouldBe` False selectorE sibling `shouldBe` False selectorE child `shouldBe` True let selectorF = compile $ Adjacent (Element [Tag "img"]) [Tag "b"] selectorF parentEl `shouldBe` False selectorF sibling `shouldBe` False selectorF child `shouldBe` True describe "Style resolution" $ do it "respects selector specificity" $ do let el = ElementNode { name = "a", namespace = "", parent = Nothing, previous = Nothing, attributes = [Attribute "class" "" "link"] } let rules = parse queryable "a.link {color: green} a {color: red}" let VarParser _ (TrivialPropertyParser style) = cascade rules el [] temp::(VarParser TrivialPropertyParser) style ! "color" `shouldBe` [Ident "green"] it "respects syntax order" $ do let el = ElementNode { name = "a", namespace = "", parent = Nothing, previous = Nothing, attributes = [Attribute "class" "" "link"] } let rules = parse queryable "a {color: red; color: green}" let VarParser _ (TrivialPropertyParser style) = cascade rules el [] temp::(VarParser TrivialPropertyParser) style ! "color" `shouldBe` [Ident "green"] let rules2 = parse queryable "a {color: red} a {color: green}" let VarParser _ (TrivialPropertyParser style2) = cascade rules2 el [] temp::(VarParser TrivialPropertyParser) style2 ! "color" `shouldBe` [Ident "green"] it "respects stylesheet precedence" $ do let el = ElementNode { name = "a", namespace = "", parent = Nothing, previous = Nothing, attributes = [Attribute "class" "" "link"] } let rules = parse (queryable {priority = 1}) "a {color: green}" let rules2 = parse (rules {priority = 2}) "a {color: red}" :: QueryableStyleSheet (VarParser TrivialPropertyParser) let VarParser _ (TrivialPropertyParser style) = cascade rules2 el [] temp::(VarParser TrivialPropertyParser) style ! "color" `shouldBe` [Ident "green"] let el' = ElementNode { name = "a", namespace = "", parent = Nothing, previous = Nothing, attributes = [Attribute "class" "" "link"] } let rules' = parse (queryable {priority = 1}) "a {color: red}" let rules2' = parse (rules' {priority = 2}) "a {color: green !important}" :: QueryableStyleSheet (VarParser TrivialPropertyParser) let VarParser _ (TrivialPropertyParser style') = cascade rules2' el' [] temp::(VarParser TrivialPropertyParser) style' ! "color" `shouldBe` [Ident "green"] it "respects overrides" $ do let el = ElementNode { name = "a", namespace = "", parent = Nothing, previous = Nothing, attributes = [Attribute "class" "" "link"] } let rules = parse queryable "a {color: red;}" let VarParser _ (TrivialPropertyParser style) = cascade rules el [("color", [Ident "green"])] temp::(VarParser TrivialPropertyParser) style ! "color" `shouldBe` [Ident "green"] describe "Parser freezes" $ do it "does not regress" $ do parse emptyStyle "output: {content: 'Output'; pitch: high}" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Tag "output"]) [] "" ] -- Turned out to just be incorrect parsing parse emptyStyle "input, output {content: attr(value)}" `shouldBe` TrivialStyleSheet [ StyleRule (Element [Tag "output"]) [ ("content", [Function "attr", Ident "value", RightParen]) ] "", StyleRule (Element [Tag "input"]) [ ("content", [Function "attr", Ident "value", RightParen]) ] "" ] it "paren balancing" $ do scanValue [RightParen] `shouldBe` ([], [RightParen]) scanValue [LeftParen] `shouldBe` ([LeftParen], []) scanValue [Function "fn", LeftParen] `shouldBe` ([Function "fn", LeftParen], []) scanValue [Function "fn", Ident "arg", LeftParen] `shouldBe` ([Function "fn", Ident "arg", LeftParen], []) describe "CSS Variables" $ do it "are captured" $ do let parser = temp :: VarParser TrivialPropertyParser vars parser `shouldBe` [] let parser1 = setVars [("--var", [Ident "value"])] parser vars parser1 `shouldBe` [("--var", [Ident "value"])] let parser2 = fromJust $ longhand parser parser1 "property" [Function "var", Ident "--var", RightParen] vars parser2 `shouldBe` [("--var", [Ident "value"])] let VarParser _ (TrivialPropertyParser style) = parser2 style ! "property" `shouldBe` [Ident "value"] let el = ElementNode { name = "a", namespace = "", parent = Nothing, previous = Nothing, attributes = [] } let rules = parse queryable "a {--var: value}" let VarParser v _ = cascade rules el [] temp v `shouldBe` [("--var", [Ident "value"])] it "applies within element" $ do let el = ElementNode { name = "a", namespace = "", parent = Nothing, previous = Nothing, attributes = [] } let rules = parse queryable "a {--link: #f00; color: var(--link)}" let VarParser vars (TrivialPropertyParser style) = cascade rules el [] temp style ! "color" `shouldBe` [Hash HId "f00"] style ! "--link" `shouldBe` [Hash HId "f00"] vars `shouldBe` [("--link", [Hash HId "f00"])] it "inherits" $ do let parent = ElementNode { name = "a", namespace = "", parent = Nothing, previous = Nothing, attributes = [] } let el = ElementNode { name = "b", namespace = "", parent = Just parent, previous = Nothing, attributes = [] } let rules = parse queryable "a {--link: #f00} b {color: var(--link)}" let VarParser vars (TrivialPropertyParser style) = cascade rules el [] $ cascade rules parent [] temp vars `shouldBe` [("--link", [Hash HId "f00"])] style ! "color" `shouldBe` [Hash HId "f00"] describe "Conditional @rules" $ do it "handles normal rules" $ do let TrivialStyleSheet styles = resolve' $ parse conditional "a {color: green}" styles `shouldBe` [StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@rule; a {color: green}" styles `shouldBe` [StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "a {color: green} @rule;" styles `shouldBe` [StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "a {color: green} @font {}" styles `shouldBe` [StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@font {} a {color: green}" styles `shouldBe` [StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] it "handles @document" $ do let TrivialStyleSheet styles = resolve' $ parse conditional "@document url(about:blank) { a {color: green} }" styles `shouldBe` [StyleRule (Element []) [] "", StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@document url(about:credits) { a {color: red} }" styles `shouldBe` [] let TrivialStyleSheet styles = resolve' $ parse conditional "@document url-prefix('about:') { a {color: green} }" styles `shouldBe` [StyleRule (Element []) [] "", StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@document url-prefix('https:') { a {color: red} }" styles `shouldBe` [] let TrivialStyleSheet styles = resolve' $ parse conditional "@document media-document('test') { a {color: green} }" styles `shouldBe` [StyleRule (Element []) [] "", StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@document media-document('other') { a {color: red} }" styles `shouldBe` [] it "handles @media" $ do let TrivialStyleSheet styles = resolve' $ parse conditional "@media test { a {color: green} }" styles `shouldBe` [StyleRule (Element []) [] "", StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@media screen { a {color: red} }" styles `shouldBe` [] let TrivialStyleSheet styles = resolve' $ parse conditional "@media test or screen { a {color: green} }" styles `shouldBe` [StyleRule (Element []) [] "", StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@media test or test {a {color: green} }" styles `shouldBe` [StyleRule (Element []) [] "", StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@media screen or screen {a {color: red} }" styles `shouldBe` [] let TrivialStyleSheet styles = resolve' $ parse conditional "@media screen or test {a {color: green} }" styles `shouldBe` [StyleRule (Element []) [] "", StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@media test and screen { a {color: red} }" styles `shouldBe` [] let TrivialStyleSheet styles = resolve' $ parse conditional "@media test and test { a {color: green} }" styles `shouldBe` [StyleRule (Element []) [] "", StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@media screen and screen { a {color: red} }" styles `shouldBe` [] let TrivialStyleSheet styles = resolve' $ parse conditional "@media screen and test {a {color: red} }" styles `shouldBe` [] let TrivialStyleSheet styles = resolve' $ parse conditional "@media 2 < 3 { a {color: green} }" styles `shouldBe` [StyleRule (Element []) [] "", StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] ""] let TrivialStyleSheet styles = resolve' $ parse conditional "@media 2 < 2 { a {color: red} }" styles `shouldBe` [] let TrivialStyleSheet styles = resolve' $ parse conditional "@media 2 < 1 { a {color: red} }" styles `shouldBe` [] it "handles @import" $ do let styles = parse conditional "@import url(about:style.css);" extractImports' styles `shouldBe` [URI "about:" Nothing "style.css" "" ""] let styles = parse conditional "@import 'about:style.css';" extractImports' styles `shouldBe` [URI "about:" Nothing "style.css" "" ""] let styles = parse conditional "@import url(about:style.css) test;" extractImports' styles `shouldBe` [URI "about:" Nothing "style.css" "" ""] let styles = parse conditional "@import url(about:style.css) screen;" extractImports' styles `shouldBe` [] -- TODO @supports is harder to test describe "CSS Counters" $ do it "Propagates other properties" $ do let textStyle = temp :: Txt.TextStyle TrivialPropertyParser let textStyle1 = fromJust $ longhand temp textStyle "foo" [Ident "bar"] style (Txt.resolve $ StyleTree textStyle1 []) `shouldBe` TrivialPropertyParser (fromList [("foo", [Ident "bar"])]) let textStyle2 = fromJust $ longhand temp textStyle1 "counter-reset" [Ident "heading"] style (Txt.resolve $ StyleTree textStyle2 []) `shouldBe` TrivialPropertyParser (fromList [("foo", [Ident "bar"])]) let textStyle3 = fromJust $ longhand temp textStyle2 "counter-set" [Ident "heading"] style (Txt.resolve $ StyleTree textStyle3 []) `shouldBe` TrivialPropertyParser (fromList [("foo", [Ident "bar"])]) let textStyle4 = fromJust $ longhand temp textStyle3 "counter-increment" [Ident "heading"] style (Txt.resolve $ StyleTree textStyle4 []) `shouldBe` TrivialPropertyParser (fromList [("foo", [Ident "bar"])]) let textStyle5 = fromJust $ longhand temp textStyle4 "white-space" [Ident "normal"] style (Txt.resolve $ StyleTree textStyle5 []) `shouldBe` TrivialPropertyParser (fromList [("foo", [Ident "bar"]), ("white-space", [Ident "normal"])]) let textStyle6 = fromJust $ longhand temp textStyle4 "white-space" [Ident "pre"] style (Txt.resolve $ StyleTree textStyle6 []) `shouldBe` TrivialPropertyParser (fromList [("foo", [Ident "bar"]), ("white-space", [Ident "nowrap"])]) let textStyle7 = fromJust $ longhand temp textStyle4 "white-space" [Ident "nowrap"] style (Txt.resolve $ StyleTree textStyle7 []) `shouldBe` TrivialPropertyParser (fromList [("foo", [Ident "bar"]), ("white-space", [Ident "nowrap"])]) let textStyle8 = fromJust $ longhand temp textStyle4 "white-space" [Ident "pre-wrap"] style (Txt.resolve $ StyleTree textStyle8 []) `shouldBe` TrivialPropertyParser (fromList [("foo", [Ident "bar"]), ("white-space", [Ident "normal"])]) let textStyle9 = fromJust $ longhand temp textStyle4 "white-space" [Ident "pre-line"] style (Txt.resolve $ StyleTree textStyle9 []) `shouldBe` TrivialPropertyParser (fromList [("foo", [Ident "bar"]), ("white-space", [Ident "normal"])]) it "Inserts counters" $ do let textStyle = temp :: Txt.TextStyle TrivialPropertyParser shorthand textStyle "content" [Function "counter", Ident "-rhaps-ol", RightParen] `shouldBe` [("content", [Function "counter", Ident "-rhaps-ol", RightParen])] shorthand textStyle "content" [Function "counters", Ident "-rhaps-ol", Comma, String ".", RightParen] `shouldBe` [("content", [Function "counters", Ident "-rhaps-ol", Comma, String ".", RightParen])] let textStyle1 = fromJust $ longhand temp textStyle "content" [Function "counter", Ident "-rhaps-ol", RightParen] style (Txt.resolve $ StyleTree textStyle1 []) `shouldBe` TrivialPropertyParser (fromList [("content", [])]) let textStyle2 = fromJust $ longhand temp textStyle1 "counter-reset" [Ident "-rhaps-ol"] style (Txt.resolve $ StyleTree textStyle2 []) `shouldBe` TrivialPropertyParser (fromList [("content", [String "0"])]) let textStyle3 = fromJust $ longhand temp textStyle1 "counter-set" [Ident "-rhaps-ol"] style (Txt.resolve $ StyleTree textStyle3 []) `shouldBe` TrivialPropertyParser (fromList [("content", [String "0"])]) let textStyle4 = fromJust $ longhand temp textStyle1 "counter-increment" [Ident "-rhaps-ol"] style (Txt.resolve $ StyleTree textStyle4 []) `shouldBe` TrivialPropertyParser (fromList [("content", [String "1"])]) styleIndex :: StyleIndex styleIndex = new queryable :: QueryableStyleSheet (VarParser TrivialPropertyParser) queryable = queryableStyleSheet emptyStyle :: TrivialStyleSheet emptyStyle = TrivialStyleSheet [] conditional :: ConditionalStyles TrivialPropertyParser conditional = conditionalStyles (fromJust $ parseURI "about:blank") "test" linkStyle :: TrivialStyleSheet linkStyle = TrivialStyleSheet [sampleRule] sampleRule :: StyleRule sampleRule = StyleRule (Element [Tag "a"]) [("color", [Ident "green"])] "" resolve' = resolve (\var -> B (var == "test")) evalToken emptyStyle where evalToken (Number _ (NVInteger x)) = N $ fromInteger x evalToken (Number _ (NVNumber x)) = N $ toRealFloat x evalToken _ = B False extractImports' = extractImports (\var -> B (var == "test")) evalToken where evalToken (Number _ (NVInteger x)) = N $ fromInteger x evalToken (Number _ (NVNumber x)) = N $ toRealFloat x evalToken _ = B False