From 4557d2b58d5e460303a8bad4d7d6c00d8f9bdb3d Mon Sep 17 00:00:00 2001 From: Ben Morris Date: Sun, 3 Mar 2019 10:50:19 -0800 Subject: [PATCH] Add 'defined' keyword --- src/Kit/Ast/ExprType.hs | 2 ++ src/Kit/Ast/TypedExpr.hs | 14 +++++++------- src/Kit/Compiler/Typers/ConvertExpr.hs | 3 +++ src/Kit/Compiler/Typers/TypeExpression.hs | 7 +++++++ src/Kit/Parser/Lexer.x | 1 + src/Kit/Parser/Parser.y | 4 ++++ src/Kit/Parser/Token.hs | 2 ++ std/kit/utils.kit | 11 +++++++++-- tests/Kit/Parser/LexerSpec.hs | 4 +++- tests/functional/defined.kit | 15 +++++++++++++++ tests/functional/defined.stdout | 3 +++ 11 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 tests/functional/defined.kit create mode 100644 tests/functional/defined.stdout diff --git a/src/Kit/Ast/ExprType.hs b/src/Kit/Ast/ExprType.hs index 0a0a3fa..a3c646c 100644 --- a/src/Kit/Ast/ExprType.hs +++ b/src/Kit/Ast/ExprType.hs @@ -97,6 +97,7 @@ data ExprType a b | StaticExpr a | Yield a | Tokens Str + | Defined (Identifier b) deriving (Eq, Generic, Show) instance (Hashable a, Hashable b) => Hashable (ExprType a b) @@ -156,6 +157,7 @@ exprDiscriminant et = case et of UnionInit _ _ -> 50 VarArgListCopy _ -> 51 StaticVtable _ -> 52 + Defined _ -> 53 x -> throwk $ InternalError ("Expression has no discriminant: " ++ show x) Nothing diff --git a/src/Kit/Ast/TypedExpr.hs b/src/Kit/Ast/TypedExpr.hs index c8f5b1f..7c2a38d 100644 --- a/src/Kit/Ast/TypedExpr.hs +++ b/src/Kit/Ast/TypedExpr.hs @@ -7,13 +7,13 @@ module Kit.Ast.TypedExpr ( makeBlock ) where -import Data.Hashable -import GHC.Generics -import Kit.Ast.ConcreteTypeBase -import Kit.Ast.Definitions -import Kit.Ast.ExprType -import Kit.Ast.Span -import Kit.Ast.Value +import Data.Hashable +import GHC.Generics +import Kit.Ast.ConcreteTypeBase +import Kit.Ast.Definitions +import Kit.Ast.ExprType +import Kit.Ast.Span +import Kit.Ast.Value type ConcreteType = ConcreteTypeBase TypedExpr diff --git a/src/Kit/Compiler/Typers/ConvertExpr.hs b/src/Kit/Compiler/Typers/ConvertExpr.hs index b73282e..456b9ec 100644 --- a/src/Kit/Compiler/Typers/ConvertExpr.hs +++ b/src/Kit/Compiler/Typers/ConvertExpr.hs @@ -202,6 +202,9 @@ convertExpr ctx tctx mod params e = do x <- r x t <- mtv return $ m (StaticExpr x) t + Defined id -> do + id <- convertIdentifier (\_ -> return TypeVoid) id + return $ m (Defined id) TypeBool VarArgListCopy s -> do return $ m (VarArgListCopy s) TypeVaList _ -> throwk $ InternalError diff --git a/src/Kit/Compiler/Typers/TypeExpression.hs b/src/Kit/Compiler/Typers/TypeExpression.hs index e373268..6ba996a 100644 --- a/src/Kit/Compiler/Typers/TypeExpression.hs +++ b/src/Kit/Compiler/Typers/TypeExpression.hs @@ -307,6 +307,13 @@ typeExpr ctx tctx mod ex@(TypedExpr { tExpr = et, tPos = pos }) = do ("static expression couldn't be evaluated at compile time") (tPos x) + (Defined (Var x)) -> do + binding <- lookupBinding ctx x + let val = BoolValue $ isJust binding + return $ (makeExprTyped (Literal val TypeBool) TypeBool pos) { + tCompileTimeValue = Just val + } + (EnumInit b _ _) -> do resolve $ TypeEq (inferredType ex) diff --git a/src/Kit/Parser/Lexer.x b/src/Kit/Parser/Lexer.x index 9b332cf..0888ff3 100644 --- a/src/Kit/Parser/Lexer.x +++ b/src/Kit/Parser/Lexer.x @@ -51,6 +51,7 @@ tokens :- continue { tok KeywordContinue } default { tok KeywordDefault } defer { tok KeywordDefer } + defined { tok KeywordDefined } do { tok KeywordDo } else { tok KeywordElse } empty { tok KeywordEmpty } diff --git a/src/Kit/Parser/Parser.y b/src/Kit/Parser/Parser.y index 7e0b6a8..8f18719 100644 --- a/src/Kit/Parser/Parser.y +++ b/src/Kit/Parser/Parser.y @@ -46,6 +46,7 @@ import Kit.Str continue {(KeywordContinue,_)} default {(KeywordDefault,_)} defer {(KeywordDefer,_)} + defined {(KeywordDefined,_)} do {(KeywordDo,_)} else {(KeywordElse,_)} empty {(KeywordEmpty,_)} @@ -680,6 +681,9 @@ BaseExpr :: {Expr} | '(' identifier "..." ')' {pe (snd $1 <+> snd $4) $ VarArgListCopy $ extract_identifier $2} | unsafe Expr {pe (snd $1 <+> pos $2) (Unsafe $2)} | sizeof TypeSpec {pe (snd $1 <+> snd $2) (SizeOf $ fst $2)} + | sizeof '(' TypeSpec ')' {pe (snd $1 <+> snd $3) (SizeOf $ fst $3)} + | defined Identifier {pe (snd $1 <+> snd $2) (Defined $ fst $2)} + | defined '(' Identifier ')' {pe (snd $1 <+> snd $3) (Defined $ fst $3)} | '(' Expr ParenthesizedExprs ')' {if null $3 then $2 {pos = snd $1 <+> snd $4} else pe (snd $1 <+> snd $4) (TupleInit ($2 : reverse $3)) } | null {pe (snd $1) Null} | empty {pe (snd $1) Empty} diff --git a/src/Kit/Parser/Token.hs b/src/Kit/Parser/Token.hs index bf5b739..325e5e6 100644 --- a/src/Kit/Parser/Token.hs +++ b/src/Kit/Parser/Token.hs @@ -38,6 +38,7 @@ data TokenClass | KeywordConst | KeywordContinue | KeywordDefault + | KeywordDefined | KeywordDefer | KeywordDo | KeywordElse @@ -140,6 +141,7 @@ instance Show TokenClass where KeywordContinue -> "continue" KeywordDefault -> "default" KeywordDefer -> "defer" + KeywordDefined -> "defined" KeywordDo -> "do" KeywordElse -> "else" KeywordEmpty -> "empty" diff --git a/std/kit/utils.kit b/std/kit/utils.kit index 780ea6d..d78d938 100644 --- a/std/kit/utils.kit +++ b/std/kit/utils.kit @@ -16,7 +16,14 @@ // } #[noreturn] function panic(msg: Const[CString], args...) { - vfprintf(stderr, msg, (args...)); - fputs("", stderr); + static if defined stderr { + // use stderr in environments where it's defined + vfprintf(stderr, msg, (args...)); + fputs("", stderr); + } else { + // fall back to stdout + vprintf(msg, (args...)); + puts(""); + } abort(); } diff --git a/tests/Kit/Parser/LexerSpec.hs b/tests/Kit/Parser/LexerSpec.hs index 7e1faf6..efa2f10 100644 --- a/tests/Kit/Parser/LexerSpec.hs +++ b/tests/Kit/Parser/LexerSpec.hs @@ -32,14 +32,16 @@ spec = parallel $ do , (FunctionArrow, sp "" 1 7 1 8) ] it "lexes keywords" $ do - lx "abstract inline for in public rule rules" + lx "abstract defined inline for in public rule rules undefined" `shouldBe` [ KeywordAbstract + , KeywordDefined , KeywordInline , KeywordFor , KeywordIn , KeywordPublic , KeywordRule , KeywordRules + , KeywordUndefined ] it "skips whitespace" $ do lx2 " \t \n \r\t \na \n\n\n\t\t\t\r\n\r\n \r \n" diff --git a/tests/functional/defined.kit b/tests/functional/defined.kit new file mode 100644 index 0000000..96f55cf --- /dev/null +++ b/tests/functional/defined.kit @@ -0,0 +1,15 @@ +function main() { + var b = defined stdout; + puts(if b then "true" else "false"); + + static if defined stdout { + puts("stdout exists"); + } else { + puts("stdout doesn't exist"); + } + static if defined stdout123987 { + puts("stdout123987 exists"); + } else { + puts("stdout123987 doesn't exist"); + } +} diff --git a/tests/functional/defined.stdout b/tests/functional/defined.stdout new file mode 100644 index 0000000..a0a571d --- /dev/null +++ b/tests/functional/defined.stdout @@ -0,0 +1,3 @@ +true +stdout exists +stdout123987 doesn't exist