diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 0836a12..cca021e 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -38,7 +38,10 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macOS-latest, windows-latest] + os: + - ubuntu-latest + - macOS-latest + # - windows-latest steps: - name: Clone project diff --git a/.gitignore b/.gitignore index ba7d650..5a82547 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,7 @@ node_modules/ /vhdl /verilog /systemverilog + +lamagraph-compiler/src/Lamagraph/Compiler/Parser.info +lamagraph-compiler/src/Lamagraph/Compiler/Parser.hs +lamagraph-compiler/src/Lamagraph/Compiler/Parser/Lexer.hs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f665979..1c9c025 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,10 +4,7 @@ repos: hooks: - id: trailing-whitespace - id: end-of-file-fixer + exclude: "ast/.*|ppr/.*" - id: check-yaml - id: fix-byte-order-marker - id: mixed-line-ending - # - repo: https://github.com/pre-commit/mirrors-prettier - # rev: v4.0.0-alpha.8 - # hooks: - # - id: prettier diff --git a/.vscode/settings.json b/.vscode/settings.json index f14d6d1..24fe307 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -37,5 +37,17 @@ }, "cSpell.words": [ "Lamagraph" - ] + ], + "[alex]": { + "editor.tabSize": 2, + "editor.rulers": [ + 120 + ] + }, + "[happy]": { + "editor.tabSize": 2, + "editor.rulers": [ + 120 + ] + } } diff --git a/fourmolu.yaml b/fourmolu.yaml index e43abe6..bd050a3 100644 --- a/fourmolu.yaml +++ b/fourmolu.yaml @@ -47,4 +47,31 @@ respectful: true fixities: [] # Module reexports Fourmolu should know about -reexports: [] +reexports: + - "module Relude exports Relude.Applicative" + - "module Relude exports Relude.Base" + - "module Relude exports Relude.Bool" + - "module Relude.Bool exports Relude.Bool.Reexport" + - "module Relude exports Relude.Container" + - "module Relude.Container exports Relude.Container.Reexport" + - "module Relude exports Relude.Debug" + - "module Relude exports Relude.DeepSeq" + - "module Relude exports Relude.Enum" + - "module Relude exports Relude.Exception" + - "module Relude exports Relude.File" + - "module Relude exports Relude.Foldable" + - "module Relude.Foldable exports Relude.Foldable.Reexport" + - "module Relude exports Relude.Function" + - "module Relude exports Relude.Functor" + - "module Relude.Functor exports Relude.Functor.Reexport" + - "module Relude exports Relude.Lifted" + - "module Relude exports Relude.List" + - "module Relude.List exports Relude.List.Reexport" + - "module Relude exports Relude.Monad" + - "module Relude.Monad exports Relude.Monad.Reexport" + - "module Relude exports Relude.Monoid" + - "module Relude exports Relude.Nub" + - "module Relude exports Relude.Numeric" + - "module Relude exports Relude.Print" + - "module Relude exports Relude.String" + - "module Relude.String exports Relude.String.Reexport" diff --git a/lamagraph-compiler/app/Main.hs b/lamagraph-compiler/app/Main.hs index 4c6b30f..895d34d 100644 --- a/lamagraph-compiler/app/Main.hs +++ b/lamagraph-compiler/app/Main.hs @@ -1,6 +1,6 @@ module Main (main) where -import Lib +import Relude main :: IO () -main = someFunc +main = pure () diff --git a/lamagraph-compiler/lamagraph-compiler.cabal b/lamagraph-compiler/lamagraph-compiler.cabal index 2711da0..79553af 100644 --- a/lamagraph-compiler/lamagraph-compiler.cabal +++ b/lamagraph-compiler/lamagraph-compiler.cabal @@ -24,16 +24,45 @@ source-repository head library exposed-modules: + Lamagraph.Compiler.Extension + Lamagraph.Compiler.Parser + Lamagraph.Compiler.Parser.Lexer + Lamagraph.Compiler.Parser.LexerTypes + Lamagraph.Compiler.Parser.LexerUtils + Lamagraph.Compiler.Parser.SrcLoc + Lamagraph.Compiler.PrettyAst + Lamagraph.Compiler.PrettyLml Lamagraph.Compiler.Syntax - Lib + Lamagraph.Compiler.Syntax.Decl + Lamagraph.Compiler.Syntax.Expr + Lamagraph.Compiler.Syntax.Extension + Lamagraph.Compiler.Syntax.Lit + Lamagraph.Compiler.Syntax.Longident + Lamagraph.Compiler.Syntax.Pat + Lamagraph.Compiler.Syntax.Type other-modules: Paths_lamagraph_compiler hs-source-dirs: src + default-extensions: + NoImplicitPrelude + OverloadedStrings + LambdaCase + NoFieldSelectors + MonoLocalBinds ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints + build-tools: + alex + , happy build-depends: - base >=4.7 && <5 - default-language: Haskell2010 + array + , base >=4.7 && <5 + , extra + , lens + , prettyprinter + , relude + , string-interpolate + default-language: GHC2021 executable lamagraph-compiler-exe main-is: Main.hs @@ -41,21 +70,56 @@ executable lamagraph-compiler-exe Paths_lamagraph_compiler hs-source-dirs: app + default-extensions: + NoImplicitPrelude + OverloadedStrings + LambdaCase + NoFieldSelectors + MonoLocalBinds ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N build-depends: - base >=4.7 && <5 + array + , base >=4.7 && <5 + , extra , lamagraph-compiler - default-language: Haskell2010 + , lens + , prettyprinter + , relude + , string-interpolate + default-language: GHC2021 test-suite lamagraph-compiler-test type: exitcode-stdio-1.0 main-is: Spec.hs other-modules: + Lamagraph.Compiler.Parser.GoldenCommon + Lamagraph.Compiler.Parser.LexerTest + Lamagraph.Compiler.Parser.ParserRoundtrip + Lamagraph.Compiler.Parser.PrettyAstGolden + Lamagraph.Compiler.Parser.PrettyLmlGolden Paths_lamagraph_compiler hs-source-dirs: test + default-extensions: + NoImplicitPrelude + OverloadedStrings + LambdaCase + NoFieldSelectors + MonoLocalBinds ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N build-depends: - base >=4.7 && <5 + array + , base >=4.7 && <5 + , extra + , filepath + , hedgehog , lamagraph-compiler - default-language: Haskell2010 + , lens + , prettyprinter + , relude + , string-interpolate + , tasty + , tasty-golden + , tasty-hedgehog + , tasty-hunit + default-language: GHC2021 diff --git a/lamagraph-compiler/package.yaml b/lamagraph-compiler/package.yaml index a01d984..bfda82f 100644 --- a/lamagraph-compiler/package.yaml +++ b/lamagraph-compiler/package.yaml @@ -21,6 +21,21 @@ description: Please see the README on GitHub at = 4.7 && < 5 + - relude + - array + - extra + - lens + - prettyprinter + - string-interpolate + +language: GHC2021 + +default-extensions: + - NoImplicitPrelude + - OverloadedStrings + - LambdaCase # Remove with GHC2024 + - NoFieldSelectors + - MonoLocalBinds # Remove with GHC2024 ghc-options: - -Wall @@ -35,6 +50,9 @@ ghc-options: library: source-dirs: src + build-tools: + - alex + - happy executables: lamagraph-compiler-exe: @@ -57,3 +75,9 @@ tests: - -with-rtsopts=-N dependencies: - lamagraph-compiler + - filepath + - tasty + - tasty-hunit + - tasty-golden + - tasty-hedgehog + - hedgehog diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Extension.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Extension.hs new file mode 100644 index 0000000..41d834a --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Extension.hs @@ -0,0 +1,79 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} + +-- | Lmlc (LamagraphML Compiler) specializations for LML AST +module Lamagraph.Compiler.Extension (Pass (..), LmlcPass (..), LmlcPs) where + +import Lamagraph.Compiler.Parser.SrcLoc +import Lamagraph.Compiler.Syntax + +data Pass = Parsed + +data LmlcPass (c :: Pass) where + LmlcPs :: LmlcPass 'Parsed + +type LmlcPs = + -- | Output of parser + LmlcPass 'Parsed + +type instance XLocated (LmlcPass p) a = Located a + +type instance XCModule (LmlcPass _) = NoExtField +type instance XXModule (LmlcPass _) = DataConCantHappen + +type instance XOpenD (LmlcPass _) = NoExtField +type instance XValD (LmlcPass _) = NoExtField +type instance XTyD (LmlcPass _) = NoExtField +type instance XXDecl (LmlcPass _) = DataConCantHappen + +type instance XOpenDecl (LmlcPass _) = NoExtField +type instance XXOpenDecl (LmlcPass _) = DataConCantHappen + +type instance XAliasDecl (LmlcPass _) = NoExtField +type instance XDataDecl (LmlcPass _) = NoExtField +type instance XXTyDecl (LmlcPass _) = DataConCantHappen + +type instance XConDecl (LmlcPass _) = NoExtField +type instance XXConDecl (LmlcPass _) = DataConCantHappen + +type instance XLmlTyVar (LmlcPass _) = NoExtField +type instance XLmlTyArrow (LmlcPass _) = NoExtField +type instance XLmlTyTuple (LmlcPass _) = NoExtField +type instance XLmlTyConstr (LmlcPass _) = NoExtField +type instance XXType (LmlcPass _) = DataConCantHappen + +type instance XLmlInt (LmlcPass _) = NoExtField +type instance XLmlInt32 (LmlcPass _) = NoExtField +type instance XLmlUInt32 (LmlcPass _) = NoExtField +type instance XLmlInt64 (LmlcPass _) = NoExtField +type instance XLmlUInt64 (LmlcPass _) = NoExtField +type instance XLmlChar (LmlcPass _) = NoExtField +type instance XLmlString (LmlcPass _) = NoExtField +type instance XXLit (LmlcPass _) = DataConCantHappen + +type instance XLmlPatAny (LmlcPass _) = NoExtField +type instance XLmlPatVar (LmlcPass _) = NoExtField +type instance XLmlPatConstant (LmlcPass _) = NoExtField +type instance XLmlPatTuple (LmlcPass _) = NoExtField +type instance XLmlPatConstruct (LmlcPass _) = NoExtField +type instance XLmlPatOr (LmlcPass _) = NoExtField +type instance XLmlPatConstraint (LmlcPass _) = NoExtField +type instance XXPat (LmlcPass _) = DataConCantHappen + +type instance XLmlExprIdent (LmlcPass _) = NoExtField +type instance XLmlExprConstant (LmlcPass _) = NoExtField +type instance XLmlExprLet (LmlcPass _) = NoExtField +type instance XLmlExprFunction (LmlcPass _) = NoExtField +type instance XLmlExprApply (LmlcPass _) = NoExtField +type instance XLmlExprMatch (LmlcPass _) = NoExtField +type instance XLmlExprTuple (LmlcPass _) = NoExtField +type instance XLmlExprConstruct (LmlcPass _) = NoExtField +type instance XLmlExprIfThenElse (LmlcPass _) = NoExtField +type instance XLmlExprConstraint (LmlcPass _) = NoExtField +type instance XXExpr (LmlcPass _) = DataConCantHappen + +type instance XLmlBind (LmlcPass _) = NoExtField +type instance XXBind (LmlcPass _) = DataConCantHappen + +type instance XLmlCase (LmlcPass _) = NoExtField +type instance XXCase (LmlcPass _) = DataConCantHappen diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Parser.y b/lamagraph-compiler/src/Lamagraph/Compiler/Parser.y new file mode 100644 index 0000000..1ae3b67 --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Parser.y @@ -0,0 +1,653 @@ +{ +{- I have no clue whether this make a difference in *any* speed, +but Happy docs recommend this flag -} +{-# OPTIONS_GHC -fglasgow-exts #-} + +{- | LamagraphML parser made with Happy +-} +module Lamagraph.Compiler.Parser (parseLamagraphML) where + +import Relude + +import qualified Data.List.NonEmpty.Extra as NE +import qualified Prelude -- Required for Happy code + +import Lamagraph.Compiler.Extension +import Lamagraph.Compiler.Parser.Lexer +import Lamagraph.Compiler.Parser.LexerTypes +import Lamagraph.Compiler.Parser.SrcLoc +import Lamagraph.Compiler.Syntax +} + +%name pLamagraphML module_expression +%tokentype { LToken } +%error { parseError } +%monad { Alex } { >>= } { return } +%lexer { lexer } { L _ TokEOF } +%expect 0 + +%token + -- Identifiers + capitalized_ident { L _ (TokIdent Capitalized _) } + lowercase_ident { L _ (TokIdent Lowercase _) } + -- Integer literals + integer_literal { L _ (TokInt _) } + int32_literal { L _ (TokInt32 _) } + uint32_literal { L _ (TokUInt32 _) } + int64_literal { L _ (TokInt64 _) } + uint64_literal { L _ (TokUInt64 _) } + -- Character liteal + char_literal { L _ (TokChar _) } + -- String literal + string_literal { L _ (TokString _) } + -- Operators + infix_symbol0 { L _ (TokInfixSymbol0 _) } + infix_symbol1 { L _ (TokInfixSymbol1 _) } + infix_symbol2 { L _ (TokInfixSymbol2 _) } + infix_symbol3 { L _ (TokInfixSymbol3 _) } + infix_symbol4 { L _ (TokInfixSymbol4 _) } + prefix_symbol { L _ (TokPrefixSymbol _) } + -- Keywords + 'and' { L _ TokAnd } + 'asr' { L _ TokAsr } + 'else' { L _ TokElse } + 'false' { L _ TokFalse } + 'fun' { L _ TokFun } + 'if' { L _ TokIf } + 'in' { L _ TokIn } + 'land' { L _ TokLand } + 'let' { L _ TokLet } + 'lor' { L _ TokLor } + 'lsl' { L _ TokLsl } + 'lsr' { L _ TokLsr } + 'lxor' { L _ TokLxor } + 'match' { L _ TokMatch } + 'mod' { L _ TokMod } + 'module' { L _ TokModule } + 'of' { L _ TokOf } + 'open' { L _ TokOpen } + 'rec' { L _ TokRec } + 'then' { L _ TokThen } + 'true' { L _ TokTrue } + 'type' { L _ TokType } + 'when' { L _ TokWhen } + 'with' { L _ TokWith } + '&&' { L _ TokBoolAnd } + '\'' { L _ TokApostrophe } + '(' { L _ TokLeftPar } + ')' { L _ TokRightPar } + '*' { L _ TokStar } + '+' { L _ TokPlus } + ',' { L _ TokComma } + '-' { L _ TokMinus } + '->' { L _ TokArrow } + ':' { L _ TokColon } + '::' { L _ TokDoubleColon } + ';' { L _ TokSemicolon } + '=' { L _ TokEq } + '[' { L _ TokLeftBracket } + ']' { L _ TokRightBracket } + '_' { L _ TokWildcard } + '.' { L _ TokDot } + '|' { L _ TokBar } + '||' { L _ TokDoubleBar } + +-- Higher in the list, lower the precendce +%nonassoc 'in' +%nonassoc ';' +%nonassoc 'let' +%nonassoc 'with' +%nonassoc 'and' +%nonassoc 'then' +%nonassoc 'else' +%left '|' +%nonassoc below_COMMA +%left ',' -- typexpr (e, e, e) +%right '->' -- typexpr (t -> t ->t) +%right '||' +%right '&&' +%left '=' infix_symbol0 +%right infix_symbol1 +%right '::' -- e :: e :: e +%left '+' '-' infix_symbol2 +%left '*' infix_symbol3 'lor' 'lxor' 'mod' 'land' -- typexpr ('t * 't) +%right infix_symbol4 'lsl' 'lsr' 'asr' +%nonassoc prec_unary_minus +%nonassoc prec_constant_constructor +%nonassoc constr_appl -- above '|' '::' ',' +%nonassoc below_DOT +%nonassoc '.' -- idents +%nonassoc '(' ')' capitalized_ident lowercase_ident integer_literal + int32_literal uint32_literal int64_literal uint64_literal + char_literal string_literal 'false' '[' ']' prefix_symbol + 'true' '_' + +%% +-- Happy does pattern-matching againt %token directives, thus we need a rule for ident +-- TODO: RdrName? +ident :: { XLocated LmlcPs Text } + : lowercase_ident { sL1 $1 $ getIdent $1 } + | capitalized_ident { sL1 $1 $ getIdent $1 } + +----------------- +-- Basic names -- +----------------- +value_name :: { XLocated LmlcPs Text } + : lowercase_ident { sL1 $1 $ getIdent $1 } + | '(' operator_name ')' { sLL $1 $3 $ unLoc $2 } + +operator_name :: { XLocated LmlcPs Text } + : prefix_symbol { sL1 $1 $ getIdent $1 } + | infix_op { $1 } + +infix_op :: { XLocated LmlcPs Text } + : infix_symbol0 { sL1 $1 $ getIdent $1 } + | infix_symbol1 { sL1 $1 $ getIdent $1 } + | infix_symbol2 { sL1 $1 $ getIdent $1 } + | infix_symbol3 { sL1 $1 $ getIdent $1 } + | infix_symbol4 { sL1 $1 $ getIdent $1 } + | '*' { sL1 $1 "*" } + | '+' { sL1 $1 "+" } + | '-' { sL1 $1 "-" } + | '=' { sL1 $1 "=" } + | '||' { sL1 $1 "||" } + | '&&' { sL1 $1 "&&" } + | 'mod' { sL1 $1 "mod" } + | 'land' { sL1 $1 "land" } + | 'lor' { sL1 $1 "lor" } + | 'lxor' { sL1 $1 "lxor" } + | 'lsl' { sL1 $1 "lsl" } + | 'lsr' { sL1 $1 "lsr" } + | 'asr' { sL1 $1 "asr" } + +constr_name :: { XLocated LmlcPs Text } + : capitalized_ident { sL1 $1 $ getIdent $1 } + +typeconstr_name :: { XLocated LmlcPs Text } + : lowercase_ident { sL1 $1 $ getIdent $1 } + +--------------------- +-- Qualified names -- +--------------------- +module_pathT :: { NonEmpty LToken } + : mkIdentRev(module_pathT, capitalized_ident) { $1 } + +value_nameT :: { LToken } + : lowercase_ident { $1 } + | '(' operator_nameT ')' { sLL $1 $3 $ unLoc $2 } + +operator_nameT :: { LToken } + : prefix_symbol { $1 } + | infix_opT { $1 } + +infix_opT :: { LToken } + : infix_symbol0 { $1 } + | infix_symbol1 { $1 } + | infix_symbol2 { $1 } + | infix_symbol3 { $1 } + | infix_symbol4 { $1 } + | '*' { $1 } + | '+' { $1 } + | '-' { $1 } + | '=' { $1 } + | '||' { $1 } + | '&&' { $1 } + | 'mod' { $1 } + | 'land' { $1 } + | 'lor' { $1 } + | 'lxor' { $1 } + | 'lsl' { $1 } + | 'lsr' { $1 } + | 'asr' { $1 } + +value_path :: { LLongident LmlcPs } + : mkIdentRev(module_pathT, value_nameT) { sLNE $1 $ getLongident (NE.reverse $1) } + -- | mkIdentRev(module_pathT, ) { sLNE $1 $ getLongident (NE.reverse $1) } + +constr :: { LLongident LmlcPs } + : mkIdentRev(module_pathT, capitalized_ident) { sLNE $1 $ getLongident (NE.reverse $1) } + | '[' ']' { sLL $1 $2 nilConstruct } + | '(' ')' { sLL $1 $2 unitConstruct } + | 'true' { sL1 $1 $ (mkLongident . pure) "true" } + | 'false' { sL1 $1 $ (mkLongident . pure) "false" } + +typeconstr :: { LLongident LmlcPs } + : mkIdentRev(module_pathT, lowercase_ident) { sLNE $1 $ getLongident (NE.reverse $1) } + +module_path :: { LLongident LmlcPs } + : module_pathT { sLNE $1 $ getLongident (NE.reverse $1) } + +---------------------- +-- Type expressions -- +---------------------- +typexpr :: { LLmlType LmlcPs } + : function_type { $1 } + +function_type :: { LLmlType LmlcPs } + : tuple_type %prec '->' { $1 } + | tuple_type '->' function_type { sLL $1 $3 (LmlTyArrow noExtField $1 $3) } + +tuple_type :: { LLmlType LmlcPs } + : atomic_type %prec below_DOT { $1 } + | atomic_type lsepBy1(atomic_type, '*') { sLNE (NE.cons $1 $2) (LmlTyTuple noExtField $1 $2) } + +delimited_type :: { LLmlType LmlcPs } + : '(' function_type ')' { sLL $1 $3 (unLoc $2) } + +atomic_type :: { LLmlType LmlcPs } + : delimited_type { $1 } + | '\'' ident { sLL $1 $2 $ LmlTyVar noExtField $2 } + | '(' sepBy2L(function_type, ',' ) ')' typeconstr { sLL $1 $4 $ LmlTyConstr noExtField $4 $2 } + | atomic_type typeconstr { sLL $1 $2 $ LmlTyConstr noExtField $2 [$1] } + | typeconstr { sL1 $1 $ LmlTyConstr noExtField $1 [] } + +--------------- +-- Constants -- +--------------- +constant :: { XLocated LmlcPs (LmlLit LmlcPs) } + : integer_literal { sL1 $1 $ LmlInt noExtField (getInt $1) } + | int32_literal { sL1 $1 $ LmlInt32 noExtField (getInt32 $1) } + | uint32_literal { sL1 $1 $ LmlUInt32 noExtField (getUInt32 $1) } + | int64_literal { sL1 $1 $ LmlInt64 noExtField (getInt64 $1) } + | uint64_literal { sL1 $1 $ LmlUInt64 noExtField (getUInt64 $1) } + | char_literal { sL1 $1 $ LmlChar noExtField (getChar $1) } + | string_literal { sL1 $1 $ LmlString noExtField (getString $1)} + +-------------- +-- Patterns -- +-------------- + +pattern :: { LLmlPat LmlcPs } + : tuple_pattern { $1 } + +pattern_comma_list :: { NonEmpty (LLmlPat LmlcPs) } + : lsepBy1Rev(simple_pattern, ',') %prec below_COMMA { NE.reverse $1 } + +tuple_pattern :: { LLmlPat LmlcPs } + : simple_pattern %prec below_COMMA { $1 } + | simple_pattern pattern_comma_list { sLNE (NE.cons $1 $2) $ LmlPatTuple noExtField $1 $2} + +delimited_pattern :: { LLmlPat LmlcPs } + : '(' tuple_pattern ')' { sLL $1 $3 (unLoc $2) } + | '(' tuple_pattern ':' typexpr ')' { sLL $1 $5 $ LmlPatConstraint noExtField $2 $4 } + +simple_pattern :: { LLmlPat LmlcPs } + : delimited_pattern { $1 } + | value_name { sL1 $1 $ LmlPatVar noExtField $1 } + | '_' { sL1 $1 $ LmlPatAny noExtField } + | constant { sL1 $1 $ LmlPatConstant noExtField (unLoc $1) } + | constr %prec below_DOT { sL1 $1 $ LmlPatConstruct noExtField $1 Nothing } + | tuple_pattern '|' tuple_pattern { sLL $1 $3 $ LmlPatOr noExtField $1 $3 } + | constr tuple_pattern %prec constr_appl { sLL $1 $2 $ LmlPatConstruct noExtField $1 (Just $2)} + | '[' sepBy1Terminated(tuple_pattern, ';') ']' { mkListPat $1 $2 $3 } + | tuple_pattern '::' tuple_pattern + { let consIdent = sL1 $2 consConstruct in + let consTuple = sLL $1 $3 $ LmlPatTuple noExtField $1 (pure $3) in + sLL $1 $3 $ LmlPatConstruct noExtField consIdent (Just consTuple) + } + +----------------- +-- Expressions -- +----------------- +expr :: { LLmlExpr LmlcPs } + : compound_expr { $1 } + +argument :: { LLmlExpr LmlcPs } + : simple_expr { $1 } + +parameter :: { LLmlPat LmlcPs } + : pattern { $1 } + +expr_comma_NE :: { NonEmpty (LLmlExpr LmlcPs) } + : lsepBy1Rev(argument, ',') %prec below_COMMA { NE.reverse $1 } + +expr_apply_NE :: { NonEmpty (LLmlExpr LmlcPs) } + : manyNERev(simple_expr) %prec below_DOT { NE.reverse $1 } + +expr_parameter_NE :: { NonEmpty (LLmlPat LmlcPs) } + : manyNERev(parameter) %prec below_DOT { NE.reverse $1 } + +type_constraint :: { LLmlType LmlcPs } + : ':' typexpr { $2 } + +compound_expr :: { LLmlExpr LmlcPs } + : simple_expr %prec below_DOT { $1 } + | simple_expr expr_comma_NE { sLNE (NE.cons $1 $2) $ LmlExprTuple noExtField $1 $2 } + | constr simple_expr %prec below_DOT { sLL $1 $2 $ LmlExprConstruct noExtField $1 (Just $2) } + | simple_expr '::' compound_expr + { let consIdent = sL1 $2 consConstruct in + let consTuple = sLL $1 $3 $ LmlExprTuple noExtField $1 (pure $3) in + sLL $1 $3 $ LmlExprConstruct noExtField consIdent (Just consTuple) + } + | prefix_symbol compound_expr + { let prefixIdent = sL1 $1 $ LmlExprIdent noExtField (getLongident (pure $1)) in + sLL $1 $2 $ LmlExprApply noExtField prefixIdent (pure $2) + } + | '-' compound_expr %prec prec_unary_minus + { let prefixIdent = sL1 $1 $ LmlExprIdent noExtField (mkLongident $ pure "~-") in + sLL $1 $2 $ LmlExprApply noExtField prefixIdent (pure $2) + } + | simple_expr infix_op compound_expr + { let infixIdent = sL1 $2 $ LmlExprIdent noExtField ((mkLongident . pure . unLoc) $2) in + sLL $1 $3 $ LmlExprApply noExtField infixIdent ($1 :| [$3]) + } + | 'if' compound_expr 'then' compound_expr 'else' compound_expr + { sLL $1 $6 $ LmlExprIfThenElse noExtField $2 $4 $6 } + | simple_expr expr_apply_NE { sLNE (NE.cons $1 $2) $ LmlExprApply noExtField $1 $2 } + | 'match' compound_expr 'with' pattern_matchingNE{ sLL $1 (last $4) $ LmlExprMatch noExtField $2 $4 } + | 'fun' expr_parameter_NE optional(type_constraint) '->' compound_expr { mkFunExpr $2 $3 $5 } + | 'let' rec sepBy1(let_binding, 'and') 'in' compound_expr { sLL $1 $5 $ LmlExprLet noExtField $2 $3 $5 } + +delimited_expr :: { LLmlExpr LmlcPs } + : '(' compound_expr ')' { sLL $1 $3 (unLoc $2) } + | '(' compound_expr ':' typexpr ')' { sLL $1 $5 $ LmlExprConstraint noExtField $2 $4 } + +-- These exprs (except delimited_expr) can be used in application w/o parentheses +simple_expr :: { LLmlExpr LmlcPs } + : delimited_expr { $1 } + | value_path { sL1 $1 $ LmlExprIdent noExtField (unLoc $1) } + | constant { sL1 $1 $ LmlExprConstant noExtField (unLoc $1) } + | '[' sepBy1Terminated(compound_expr, ';') ']' { mkListExpr $1 $2 $3 } + | constr %prec prec_constant_constructor { sL1 $1 $ LmlExprConstruct noExtField $1 Nothing } + +when_expr :: { LLmlExpr LmlcPs } + : 'when' expr { $2 } + +pattern_matching :: { LLmlCase LmlcPs } + : pattern optional(when_expr) '->' expr { sLL $1 $4 $ LmlCase noExtField $1 $2 $4 } + +pattern_matchingNE :: { NonEmpty (LLmlCase LmlcPs) } + : lsepBy1PreceededRev(pattern_matching, '|') %shift { NE.reverse $1 } + +let_binding :: { LLmlBind LmlcPs } + : pattern '=' expr { sLL $1 $3 $ LmlBind noExtField $1 $3 } + | value_name expr_parameter_NE optional(type_constraint) '=' expr + { let patIdent = sL1 $1 $ LmlPatVar noExtField $1 in + sLL $1 $5 $ LmlBind noExtField patIdent (mkFunExpr $2 $3 $5) + } + +---------------------- +-- Type definitions -- +---------------------- + +-- type-information rule in inlined here +typedef :: { LTyDecl LmlcPs } + : optional(type_params) typeconstr_name {- empty -} + { let typeParams = maybe [] toList $1 in + sMNELL $1 $2 $2 $ DataDecl noExtField $2 typeParams [] + } + | optional(type_params) typeconstr_name '=' type_equation + { let typeParams = maybe [] toList $1 in + sMNELL $1 $2 $4 $ AliasDecl noExtField $2 typeParams $4 + } + | optional(type_params) typeconstr_name '=' type_representation + { let typeParams = maybe [] toList $1 in + sMNELL $1 $2 (last $4) $ DataDecl noExtField $2 typeParams (toList $4) + } + +type_equation :: { LLmlType LmlcPs } + : typexpr { $1 } + +-- For some LR related reason following code will give weird shift/reduce conflicts +-- -- type_representation :: { NonEmpty ConstructorDeclaration } +-- -- : lsepBy1Preceeded(constr_decl, '|') { $1 } +-- One below -- won't + +type_representationRev :: { NonEmpty (LConDecl LmlcPs) } + : constr_decl { pure $1 } + | '|' constr_decl { pure $2 } + | type_representationRev '|' constr_decl { NE.cons $3 $1 } + +type_representation :: { NonEmpty (LConDecl LmlcPs) } + : type_representationRev { NE.reverse $1 } + +type_params :: { NonEmpty (LLmlType LmlcPs) } + : type_param { pure $1 } + | '(' sepBy1(type_param, ',') ')' { $2 } + +type_param :: { LLmlType LmlcPs } + : '\'' ident { sLL $1 $2 $ LmlTyVar noExtField $2 } + +constr_args :: { NonEmpty (LLmlType LmlcPs) } + : sepBy1(atomic_type, '*') { $1 } + +of_constr_args :: { NonEmpty (LLmlType LmlcPs) } + : 'of' constr_args { $2 } + +constr_decl :: { LConDecl LmlcPs } + : constr_name optional(of_constr_args) + { sLMNEL $1 $2 $1 $ ConDecl noExtField $1 (maybe [] toList $2) } + | '[' ']' optional(of_constr_args) + { let ident = sLL $1 $2 "[]" in + sLMNEL $1 $3 $2 $ ConDecl noExtField ident (maybe [] toList $3) + } + | '(' '::' ')' optional(of_constr_args) + { let ident = sLL $1 $3 "::" in + sLMNEL $1 $4 $3 $ ConDecl noExtField ident (maybe [] toList $4) + } + +------------------------------ +-- Declarations and modules -- +------------------------------ +module_expression :: { LmlModule LmlcPs } + : optional(module_definition) many(module_item) + { LmlModule{ _lmlModExt = noExtField, _lmlModName = $1, _lmlModDecls = $2 } } + +-- FIXME: Store 'module' location +module_definition :: { LLongident LmlcPs } + : 'module' module_path { $2 } + +open_declaration :: { LOpenDecl LmlcPs } + : 'open' module_path { sLL $1 $2 $ OpenDecl noExtField $2 } + +module_item :: { LLmlDecl LmlcPs } + : open_declaration { sL1 $1 $ OpenD noExtField (unLoc $1) } + | 'let' rec sepBy1(let_binding, 'and') { sLL $1 (last $3) $ ValD noExtField $2 $3 } + | 'type' sepBy1(typedef, 'and') { sLL $1 (last $2) $ TyD noExtField $2 } + +------------- +-- Helpers -- +------------- +optional(p) + : {- empty -} { Nothing } + | p { Just $1 } + +sepBy1Rev(p, sep) + : p { pure $1 } + | sepBy1Rev(p, sep) sep p { NE.cons $3 $1 } + +sepBy1(p, sep) : sepBy1Rev(p, sep) { NE.reverse $1 } + +manyRev(p) + : {- empty -} { [] } + | manyRev(p) p { $2 : $1 } + +many(p) : manyRev(p) { reverse $1 } + +manyNERev(p) + : p { pure $1 } + | manyNERev(p) p { NE.cons $2 $1 } + +manyNE(p) : manyNERev(p) { NE.reverse $1 } + +sepBy2LRev(p, sep) + : sepBy2LRev(p, sep) sep p { $3 : $1 } + | p sep p { [$3, $1]} + +sepBy2L(p, sep) : sepBy2LRev(p, sep) { reverse $1} + +lsepBy1Rev(p, sep) + : sep p { pure $2 } + | lsepBy1Rev(p, sep) sep p { NE.cons $3 $1 } + +lsepBy1(p, sep) : lsepBy1Rev(p, sep) { NE.reverse $1 } + +rec :: { RecFlag } + : {- empty -} { NonRecursive } + | 'rec' { Recursive } + +mkIdentRev(prefix,final) + : final { pure $1 } + | prefix '.' final { NE.cons $3 $1 } + +mkIdent(prefix, final) : mkIdentRev(prefix,final) { NE.reverse $1 } + +sepBy1Terminated(p, sep) + : p optional(sep) { pure $1 } + | p sep sepBy1Terminated(p, sep) { NE.cons $1 $3 } + +lsepBy1PreceededRev(p, sep) + : optional(sep) p { pure $2 } + | lsepBy1PreceededRev(p, sep) sep p { NE.cons $3 $1 } + +lsepBy1Preceeded(p, sep) : lsepBy1PreceededRev(p, sep) { NE.reverse $1 } + +{ +-- TODO: Find another way to print a token +parseError :: LToken -> Alex a +parseError token = do + (AlexPn _ line column, _, _, str) <- alexGetInput + alexError $ "parse error at line " ++ (show line) ++ ", column " ++ (show column) ++ ": " ++ (show (unLoc token)) + +lexer :: (LToken -> Alex a) -> Alex a +lexer = (alexMonadScan >>=) + +getIdent :: LToken -> Text +getIdent (L _ (TokIdent _ val)) = val +getIdent (L _ (TokInfixSymbol0 val)) = val +getIdent (L _ (TokInfixSymbol1 val)) = val +getIdent (L _ (TokInfixSymbol2 val)) = val +getIdent (L _ (TokInfixSymbol3 val)) = val +getIdent (L _ (TokInfixSymbol4 val)) = val +getIdent (L _ TokStar) = "*" +getIdent (L _ TokPlus) = "+" +getIdent (L _ TokMinus) = "-" +getIdent (L _ TokEq) = "=" +getIdent (L _ TokDoubleBar) = "||" +getIdent (L _ TokBoolAnd) = "&&" +getIdent (L _ TokMod) = "mod" +getIdent (L _ TokLand) = "land" +getIdent (L _ TokLor) = "lor" +getIdent (L _ TokLxor) = "lxor" +getIdent (L _ TokLsl) = "lsl" +getIdent (L _ TokLsr) = "lsr" +getIdent (L _ TokAsr) = "asr" +getIdent (L _ (TokPrefixSymbol val)) = val +-- getIdent (L _ TokBar) = "|" + +getLongident :: NonEmpty LToken -> Longident +getLongident = mkLongident . fmap getIdent + +getInt :: LToken -> Int +getInt (L _ (TokInt val)) = val + +getInt32 :: LToken -> Int32 +getInt32 (L _ (TokInt32 val)) = val + +getUInt32 :: LToken -> Word32 +getUInt32 (L _ (TokUInt32 val)) = val + +getInt64 :: LToken -> Int64 +getInt64 (L _ (TokInt64 val)) = val + +getUInt64 :: LToken -> Word64 +getUInt64 (L _ (TokUInt64 val)) = val + +getChar :: LToken -> Char +getChar (L _ (TokChar val)) = val + +getString :: LToken -> Text +getString (L _ (TokString val)) = val + +nilConstruct :: Longident +nilConstruct = (mkLongident . pure) "[]" + +consConstruct :: Longident +consConstruct = (mkLongident . pure) "::" + +unitConstruct :: Longident +unitConstruct = (mkLongident . pure) "()" + +-- Combining helpers +{-# INLINE comb2 #-} +comb2 :: Located a -> Located b -> SrcSpan +comb2 a b = a `seq` b `seq` combineLocs a b + +-- | Strict 'GenLocated' constructor +{-# INLINE sL #-} +sL :: l -> e -> GenLocated l e +sL loc e = loc `seq` e `seq` L loc e + +-- | Combine first and last locations from 'NonEmpty' +{-# INLINE sLNE #-} +sLNE :: NonEmpty (Located a) -> b -> Located b +sLNE ne = sL (combineLocs (head ne) (last ne)) + +-- | Combine two locations +{-# INLINE sLL #-} +sLL :: Located a -> Located b -> c -> Located c +sLL a b = sL (comb2 a b) + +-- | Repack 'Located' +{-# INLINE sL1 #-} +sL1 :: Located a -> b -> Located b +sL1 a = sL (getLoc a) + +-- | Combine two location when first is @'Maybe' ('NonEmpty' a)@. +-- If first is 'Nothing', then second one is selected. +-- Third is always used as the last. +{-# INLINE sMNELL #-} +sMNELL :: Maybe (NonEmpty (Located a)) -> Located b -> Located c -> d -> Located d +sMNELL a b c = sL outLoc + where + leftLoc :: SrcSpan + leftLoc = maybe (getLoc b) (getLoc . head) a + outLoc :: SrcSpan + outLoc = combineSrcSpans leftLoc (getLoc c) + +-- | Combine two location when second is @'Maybe' ('NonEmpty' a)@. +-- If second is 'Nothing', then third one is selected. +-- First is always used as the leader. +{-# INLINE sLMNEL #-} +sLMNEL :: Located a -> Maybe (NonEmpty (Located b)) -> Located c -> d -> Located d +sLMNEL a b c = sL outLoc + where + rightLoc :: SrcSpan + rightLoc = maybe (getLoc c) (getLoc . last) b + outLoc :: SrcSpan + outLoc = combineSrcSpans (getLoc a) rightLoc + +mkListPat :: LToken -> NonEmpty (LLmlPat LmlcPs) -> LToken -> LLmlPat LmlcPs +mkListPat lBracket list rBracket = foldr helper init list + where + init :: LLmlPat LmlcPs + init = sL1 rBracket $ LmlPatConstruct noExtField (sL generatedSrcSpan nilConstruct) Nothing + helper :: LLmlPat LmlcPs -> LLmlPat LmlcPs -> LLmlPat LmlcPs + helper x acc = sLL x rBracket $ LmlPatConstruct noExtField (sL generatedSrcSpan consConstruct) (Just pat) + where + pat = sLL x acc $ LmlPatTuple noExtField x (pure acc) + +mkListExpr :: LToken -> NonEmpty (LLmlExpr LmlcPs) -> LToken -> LLmlExpr LmlcPs +mkListExpr lBracket list rBracket = foldr helper init list + where + init :: LLmlExpr LmlcPs + init = sL1 rBracket $ LmlExprConstruct noExtField (sL generatedSrcSpan nilConstruct) Nothing + helper :: LLmlExpr LmlcPs -> LLmlExpr LmlcPs -> LLmlExpr LmlcPs + helper x acc = sLL x rBracket $ LmlExprConstruct noExtField (sL generatedSrcSpan consConstruct) (Just expr) + where + expr = sLL x acc $ LmlExprTuple noExtField x (pure acc) + +mkFunExpr :: NonEmpty (LLmlPat LmlcPs) -> Maybe (LLmlType LmlcPs) -> LLmlExpr LmlcPs -> LLmlExpr LmlcPs +mkFunExpr pats mType rhsExpr = foldr helper init pats + where + init :: LLmlExpr LmlcPs + init = case mType of + Just typ -> sLL typ rhsExpr $ LmlExprConstraint noExtField rhsExpr typ + Nothing -> rhsExpr + helper :: LLmlPat LmlcPs -> LLmlExpr LmlcPs -> LLmlExpr LmlcPs + helper pat acc = sLL pat acc $ LmlExprFunction noExtField pat acc + +-- | Parser entry point +parseLamagraphML :: Text -> Either String (LmlModule LmlcPs) +parseLamagraphML text = runAlex text pLamagraphML +} diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Parser/Lexer.x b/lamagraph-compiler/src/Lamagraph/Compiler/Parser/Lexer.x new file mode 100644 index 0000000..0adf5a7 --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Parser/Lexer.x @@ -0,0 +1,299 @@ +-------------------------------------- +-- Alex "Haskell code fragment top" -- +-------------------------------------- +{ +{- Some of the Alex generated code contains @undefinded@ which is considered +deprecated in Relude +-} +{-# OPTIONS_GHC -Wno-deprecations #-} +{- Because of the active use of lenses, in this project field selectors are generally +disabled, but because Alex relies on them, they must be turned on explicitly +-} +{-# LANGUAGE FieldSelectors #-} + +{- | Module with Alex parser + +Some of the exports aren't used in other modules, but are useful in docs. +-} +module Lamagraph.Compiler.Parser.Lexer ( + Byte, + AlexInput, + AlexPosn(..), + runAlex, + Alex, + alexError, + alexGetInput, + alexMonadScan +) where + +import Relude +{- These functions must be used only for crashing the entire app, +because of the bug in Alex, not in this code +-} +import Relude.Unsafe (fromJust, read) + +import Control.Lens +import qualified Data.Text as Text + +import Lamagraph.Compiler.Parser.LexerTypes +import Lamagraph.Compiler.Parser.SrcLoc +} +-------------------- +-- Alex "Wrapper" -- +-------------------- +%wrapper "monadUserState-strict-text" + +--------------------------------- +-- Alex "Character set macros" -- +--------------------------------- + +$digit = [0-9] +$letter = [a-zA-Z] +$capital_letter = [A-Z] +$lowercase_letter = [a-z] + +$escape_sequence = [\\ \" \' \n] +$regular_char = [\ -\~] # $escape_sequence -- # is a set difference + +$operator_char = [\! \$ \% & \* \+ \. \/ \: \< \= \> \? \@ \^ \| \~] + +-------------------------------------- +-- Alex "Regular expression macros" -- +-------------------------------------- + +-- Identifiers +@ident_tail = ( $letter | $digit | \_ | \' )* +@capitalized_ident = $capital_letter @ident_tail +@lowercase_ident = ( + ( $lowercase_letter @ident_tail) + -- In constrast to the grammar @_@ is reserved as a wildcard and thus cannot be identifier + | ( \_ ( $letter | $digit ) @ident_tail ) +) + +-- Integer literals +@integer_literal = \-? $digit ( $digit | \_ )* +@int32_literal = @integer_literal l +@uint32_literal = @integer_literal ul +@int64_literal = @integer_literal L +@uint64_literal = @integer_literal UL + +-- Operators +@infix_symbol = ( \= | \< | \> | \@ | \^ | \| | & | \+ | \- | \* | \/ | \$ | \% ) ( $operator_char )* +@prefix_symbol = ( + ( \! ( $operator_char )* ) + | ( ( \? | \~ ) ( $operator_char )+ ) +) + +----------------------- +-- Alex "Identifier" -- +----------------------- + +lamagraphml :- + +------------------ +-- Alex "Rules" -- +------------------ + +<0> $white+ ; + +<0> "(*" { enterNewComment `andBegin` state_comment } +<0> "*)" { alexErrorPos "Closing comment before opening" } + "(*" { embedComment } + "*)" { unembedComment } + . ; + \n ; + +<0> "--" .* ; + +<0> "and" { tok TokAnd } +<0> "asr" { tok TokAsr } +<0> "else" { tok TokElse } +<0> "false" { tok TokFalse } +<0> "fun" { tok TokFun } +<0> "if" { tok TokIf } +<0> "in" { tok TokIn } +<0> "land" { tok TokLand } +<0> "let" { tok TokLet } +<0> "lor" { tok TokLor } +<0> "lsl" { tok TokLsl } +<0> "lsr" { tok TokLsr } +<0> "lxor" { tok TokLxor } +<0> "match" { tok TokMatch } +<0> "mod" { tok TokMod } +<0> "module" { tok TokModule } +<0> "of" { tok TokOf } +<0> "open" { tok TokOpen } +<0> "rec" { tok TokRec } +<0> "then" { tok TokThen } +<0> "true" { tok TokTrue } +<0> "type" { tok TokType } +<0> "when" { tok TokWhen } +<0> "with" { tok TokWith } +<0> "&&" { tok TokBoolAnd } +<0> "'" { tok TokApostrophe } +<0> "(" { tok TokLeftPar } +<0> ")" { tok TokRightPar } +<0> "*" { tok TokStar } +<0> "+" { tok TokPlus } +<0> "," { tok TokComma } +<0> "-" { tok TokMinus } +<0> "->" { tok TokArrow } +<0> ":" { tok TokColon } +<0> "::" { tok TokDoubleColon } +<0> ";" { tok TokSemicolon } +<0> "=" { tok TokEq } +<0> "[" { tok TokLeftBracket } +<0> "]" { tok TokRightBracket } +<0> "_" { tok TokWildcard } +<0> "." { tok TokDot } +<0> "|" { tok TokBar } +<0> "||" { tok TokDoubleBar } + +<0> @capitalized_ident { tokAnyIdent (TokIdent Capitalized) } +<0> @lowercase_ident { tokAnyIdent (TokIdent Lowercase) } + +<0> @uint64_literal { tokAnyInt TokUInt64 } +<0> @uint32_literal { tokAnyInt TokUInt32 } +<0> @int64_literal { tokAnyInt TokInt64 } +<0> @int32_literal { tokAnyInt TokInt32 } +<0> @integer_literal { tokAnyInt TokInt } + +<0> \' \\n \' { tokEscapedChar '\n' } +<0> \' \\\' \' { tokEscapedChar '\'' } +<0> \' \\\" \' { tokEscapedChar '\"' } +<0> \' \\\\ \' { tokEscapedChar '\\' } +<0> \' $regular_char \' { tokRegularChar } + +<0> \" { enterNewString `andBegin` state_string } + \\n { addCharToString '\n' } + \\\" { addCharToString '\"' } + \\\' { addCharToString '\'' } + \\\\ { addCharToString '\\' } + \\ { alexErrorPos "Unfinished escape character" } + \" { leaveString `andBegin` 0 } + $regular_char { addCurrentToString } + +<0> \*\* ( $operator_char )* { tokAnyIdent TokInfixSymbol4 } +<0> (\* | \/ | \%) ( $operator_char )* { tokAnyIdent TokInfixSymbol3 } +<0> (\+ | \-) ( $operator_char )* { tokAnyIdent TokInfixSymbol2 } +<0> (\@ | \^) ( $operator_char )* { tokAnyIdent TokInfixSymbol1 } +<0> (\= | \< | \> | \| | & | \$) ( $operator_char )* { tokAnyIdent TokInfixSymbol0 } + +<0> @prefix_symbol { tokAnyIdent TokPrefixSymbol } + +----------------------------------------- +-- Alex "Haskell code fragment bottom" -- +----------------------------------------- +{ +instance MonadState AlexUserState Alex where + get :: Alex AlexUserState + get = Alex $ \st -> Right (st, alex_ust st) + put :: AlexUserState -> Alex () + put newState = Alex $ \st -> Right (st{alex_ust = newState}, ()) + +-- FIXME: Handle filenames correctly +alexPosnToSrcLoc :: AlexPosn -> SrcLoc +alexPosnToSrcLoc (AlexPn _ line column) = mkSrcLoc "" line column + +alexPosnToSrcSpan :: AlexPosn -> AlexPosn -> SrcSpan +alexPosnToSrcSpan pos1 pos2 = mkSrcSpan (alexPosnToSrcLoc pos1) (alexPosnToSrcLoc pos2) + +getEndPos :: AlexInput -> Int -> AlexPosn +getEndPos (startPosn, _, _, str) len = Text.foldl' alexMove startPosn $ Text.take len str + +alexEOF :: Alex LToken +alexEOF = do + startCode <- alexGetStartCode + when (startCode == state_comment) $ (alexError "lexical error: EOF while reading comment") + when (startCode == state_string) $ (alexError "lexical error: EOF while reading string") + (pos, _, _, _) <- alexGetInput + return $ L (alexPosnToSrcSpan pos pos) TokEOF + +enterNewComment :: AlexAction LToken +enterNewComment input len = do + lexerCommentDepth .= 1 + skip input len + +embedComment :: AlexAction LToken +embedComment input len = do + lexerCommentDepth += 1 + skip input len + +unembedComment :: AlexAction LToken +unembedComment input len = do + lexerCommentDepth -= 1 + commentDepth <- use lexerCommentDepth + when (commentDepth == 0) $ alexSetStartCode 0 + skip input len + +tokAnyIdent :: (Text -> Token) -> AlexAction LToken +tokAnyIdent ctor input@(startPosn, _, _, str) len = do + return $ L + (alexPosnToSrcSpan startPosn $ getEndPos input len) + (ctor $ Text.take len str) + +tokAnyInt :: Read a => (a -> Token) -> AlexAction LToken +tokAnyInt ctor input@(startPosn, _, _, str) len = do + let num = read $ toString $ Text.dropWhileEnd isIntSuffix $ Text.take len str + return $ L + (alexPosnToSrcSpan startPosn $ getEndPos input len) + (ctor num) + where + isIntSuffix :: Char -> Bool + isIntSuffix 'u' = True + isIntSuffix 'U' = True + isIntSuffix 'l' = True + isIntSuffix 'L' = True + isIntSuffix _ = False + +tok :: Token -> AlexAction LToken +tok ctor input@(startPosn, _, _, _) len = do + return $ L (alexPosnToSrcSpan startPosn $ getEndPos input len) ctor + +enterNewString :: AlexAction LToken +enterNewString (startPosn, _, _, _) _ = do + lexerStringStartPos .= Just (alexPosnToSrcLoc startPosn) + lexerStringValue .= "" + alexMonadScan + +addCharToString :: Char -> AlexAction LToken +addCharToString char (_, _, _, _) _ = do + lexerStringValue <>= Text.singleton char + alexMonadScan + +leaveString :: AlexAction LToken +leaveString input@(_, _, _, _) len = do + tokenType' <- use lexerStringValue + startPos' <- use lexerStringStartPos + + lexerStringStartPos .= Nothing + lexerStringValue .= "" + + return $ L + (mkSrcSpan (fromJust startPos') (alexPosnToSrcLoc $ getEndPos input len)) + (TokString tokenType') + +addCurrentToString :: AlexAction LToken +addCurrentToString input@(_, _, _, str) len = do + let char = Text.head $ Text.take len str + addCharToString char input len + +tokRegularChar :: AlexAction LToken +tokRegularChar input@(startPosn, _, _, str) len = do + let fullStr = Text.take len str + return $ L + (alexPosnToSrcSpan startPosn $ getEndPos input len) + (TokChar $ Text.head $ Text.dropAround (== '\'') fullStr) + +tokEscapedChar :: Char -> AlexAction LToken +tokEscapedChar char input@(startPosn, _, _, _) len = do + return $ L + (alexPosnToSrcSpan startPosn $ getEndPos input len) + (TokChar char) + +-- TODO: Investigate 'String' there +alexErrorPos :: String -> AlexAction a +alexErrorPos msg ((AlexPn _ line column), _, _, _) _ = Alex $ const $ Left fullMsg + where + fullMsg = "lexical error at line " ++ (show line) ++ ", column " ++ (show column) ++ ": " ++ msg +} diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Parser/LexerTypes.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Parser/LexerTypes.hs new file mode 100644 index 0000000..44a2515 --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Parser/LexerTypes.hs @@ -0,0 +1,131 @@ +{-# LANGUAGE TemplateHaskell #-} + +-- | Module for types produced by lexer +module Lamagraph.Compiler.Parser.LexerTypes ( + IdentType (..), + Token (..), + LToken, + AlexUserState (..), + alexInitUserState, + lexerCommentDepth, + lexerStringStartPos, + lexerStringValue, +) where + +import Relude + +import Control.Lens + +import Lamagraph.Compiler.Parser.SrcLoc + +data AlexUserState = AlexUserState + { _lexerCommentDepth :: Int + , _lexerStringStartPos :: Maybe SrcLoc + -- ^ t'SrcLoc' here allows to untie cyclic dependency on 'Lamagraph.Compiler.Parser.Lexer.AlexPosn' + , _lexerStringValue :: Text + } + deriving (Eq, Show) +makeLenses ''AlexUserState + +alexInitUserState :: AlexUserState +alexInitUserState = + AlexUserState + { _lexerCommentDepth = 0 + , _lexerStringStartPos = Nothing + , _lexerStringValue = "" + } + +data IdentType = Capitalized | Lowercase + deriving (Eq, Show) + +data Token + = TokIdent IdentType Text + | TokInt Int + | TokInt32 Int32 + | TokUInt32 Word32 + | TokInt64 Int64 + | TokUInt64 Word64 + | {- Character literals -} + TokChar Char + | {- String literals -} + TokString Text + | {- Operators -} + + -- | @( = | \< | \> | __|__ | \& | $ ) /operator-char/*@ + TokInfixSymbol0 Text + | -- | @( \@ | \^ ) /operator-char/*@ + TokInfixSymbol1 Text + | -- | @( + | - ) /operator-char/*@ + TokInfixSymbol2 Text + | -- | @( * | \/ | % ) /operator-char/* | lor | lxor | mod | land@ + TokInfixSymbol3 Text + | -- | @** /operator-char/* | lsl | lsr | asr@ + TokInfixSymbol4 Text + | TokPrefixSymbol Text + | {- Keywords-} + TokAnd + | TokAsr + | TokElse + | TokFalse + | TokFun + | TokIf + | TokIn + | TokLand + | TokLet + | TokLor + | TokLsl + | TokLsr + | TokLxor + | TokMatch + | TokMod + | TokModule + | TokOf + | TokOpen + | TokRec + | TokThen + | TokTrue + | TokType + | TokWhen + | TokWith + | -- | @&&@ + TokBoolAnd + | -- | @'@ + TokApostrophe + | -- | @(@ + TokLeftPar + | -- | @)@ + TokRightPar + | -- | @*@ + TokStar + | -- | @+@ + TokPlus + | -- | @,@ + TokComma + | -- | @-@ + TokMinus + | -- | @->@ + TokArrow + | -- | @:@ + TokColon + | -- | @::@ + TokDoubleColon + | -- | @;@ + TokSemicolon + | -- | @=@ + TokEq + | -- | @[@ + TokLeftBracket + | -- | @]@ + TokRightBracket + | -- | @_@ + TokWildcard + | -- | @.@ + TokDot + | -- | @|@ + TokBar + | -- | @||@ + TokDoubleBar + | TokEOF + deriving (Eq, Show) + +type LToken = Located Token diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Parser/LexerUtils.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Parser/LexerUtils.hs new file mode 100644 index 0000000..68ddd54 --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Parser/LexerUtils.hs @@ -0,0 +1,30 @@ +{- | Module with functions for working with lexer token stream + +For now they are used only in tests. +-} +module Lamagraph.Compiler.Parser.LexerUtils (scanner, getTokenTypes, getTokenTypesFromText) where + +import Relude + +import Control.Lens + +import Lamagraph.Compiler.Parser.Lexer +import Lamagraph.Compiler.Parser.LexerTypes +import Lamagraph.Compiler.Parser.SrcLoc + +scanner :: Text -> Either String [LToken] +scanner text = runAlex text loop + where + loop = do + readToken <- alexMonadScan + if readToken ^. _L . _2 == TokEOF + then return [readToken] + else do + tokens <- loop + return (readToken : tokens) + +getTokenTypes :: Either String [LToken] -> Either String [Token] +getTokenTypes tokens = tokens & _Right %~ toListOf (traverse . _L . _2) + +getTokenTypesFromText :: Text -> Either String [Token] +getTokenTypesFromText = getTokenTypes . scanner diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Parser/SrcLoc.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Parser/SrcLoc.hs new file mode 100644 index 0000000..892c15d --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Parser/SrcLoc.hs @@ -0,0 +1,198 @@ +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TemplateHaskell #-} + +{- | This module contain types that describe positions of text in source files. +This types are used instead of 'Lamagraph.Compiler.Parser.Lexer.AlexPosn' outside of the internals of the lexer. + +Inspired by GHC's SrcLoc.hs. +Thus, if some function is needed for compiler, please, consult with GHC first. +-} +module Lamagraph.Compiler.Parser.SrcLoc ( + -- * SrcLoc + RealSrcLoc, -- abstract + SrcLoc (..), + + -- ** Constructing SrcLoc + mkSrcLoc, + mkRealSrcLoc, + generatedSrcLoc, + mkBadSrcLoc, + + -- ** Accessing RealSrcLoc + _RealSrcLoc', + + -- * SrcSpan + RealSrcSpan, -- abstract + SrcSpan (..), + UnhelpfulSpanReason (..), + + -- ** Constructing SrcSpan + mkBadSrcSpan, + mkSrcSpan, + mkRealSrcSpan, + generatedSrcSpan, + combineSrcSpans, + + -- ** Accessing RealSrcSpan + srcSpanFile, + srcSpanSLine, + srcSpanSColumn, + srcSpanELine, + srcSpanEColumn, + + -- * Located + Located, + RealLocated, + GenLocated (..), + + -- ** Operations on Located + mkBadLocated, + combineLocs, + + -- ** Accessing Located + _L, + unLoc, + getLoc, + + -- * Parser locations + combineRealSrcSpans, +) where + +import Relude + +import Control.Lens + +{- | Real Source Location + +Represents a single point in a file +-} +data RealSrcLoc + = RealSrcLoc' + Text -- Filename + !Int -- Line number, starts from 1 + !Int -- Column number, starts from 1 + deriving (Eq, Show) + +{- | Example access + x ^. _RealSrcLoc' . _1 +-} +makePrisms ''RealSrcLoc + +-- | Source Location +data SrcLoc + = RealSrcLoc !RealSrcLoc + | UnhelpfulLoc Text -- With a reason why + deriving (Show, Eq) + +-- makePrisms ''SrcLoc + +mkSrcLoc :: Text -> Int -> Int -> SrcLoc +mkSrcLoc file line column = RealSrcLoc (mkRealSrcLoc file line column) + +mkRealSrcLoc :: Text -> Int -> Int -> RealSrcLoc +mkRealSrcLoc = RealSrcLoc' + +generatedSrcLoc :: SrcLoc +generatedSrcLoc = UnhelpfulLoc "" + +mkBadSrcLoc :: Text -> SrcLoc +mkBadSrcLoc = UnhelpfulLoc + +{- | Real Source Span + +Represents a span in a source file using a pair of (line, column) coordinates +-} +data RealSrcSpan + = RealSrcSpan' + { _srcSpanFile :: Text + , _srcSpanSLine :: !Int + , _srcSpanSColumn :: !Int + , _srcSpanELine :: !Int + , _srcSpanEColumn :: !Int + } + deriving (Eq, Show) + +makeLenses ''RealSrcSpan + +{- | Source Span + +A 'SrcSpan' represents either "good" portion of a file +or a description of a "bad" span. +-} +data SrcSpan = RealSrcSpan RealSrcSpan | UnhelpfulSpan UnhelpfulSpanReason deriving (Show, Eq) + +data UnhelpfulSpanReason = UnhelpfulGenerated | UnhelpfulOther Text deriving (Show, Eq) + +generatedSrcSpan :: SrcSpan +generatedSrcSpan = UnhelpfulSpan UnhelpfulGenerated + +mkBadSrcSpan :: Text -> SrcSpan +mkBadSrcSpan = UnhelpfulSpan . UnhelpfulOther + +mkRealSrcSpan :: RealSrcLoc -> RealSrcLoc -> RealSrcSpan +mkRealSrcSpan loc1 loc2 = RealSrcSpan'{..} + where + _srcSpanFile = loc1 ^. _RealSrcLoc' . _1 + _srcSpanSLine = loc1 ^. _RealSrcLoc' . _2 + _srcSpanSColumn = loc1 ^. _RealSrcLoc' . _3 + _srcSpanELine = loc2 ^. _RealSrcLoc' . _2 + _srcSpanEColumn = loc2 ^. _RealSrcLoc' . _3 + +-- | Create 'SrcSpan' between two positions in a file +mkSrcSpan :: SrcLoc -> SrcLoc -> SrcSpan +mkSrcSpan (UnhelpfulLoc str) _ = mkBadSrcSpan str +mkSrcSpan _ (UnhelpfulLoc str) = mkBadSrcSpan str +mkSrcSpan (RealSrcLoc loc1) (RealSrcLoc loc2) = RealSrcSpan $ mkRealSrcSpan loc1 loc2 + +{- | Combines two 'RealSrcSpan's into one that covers both original one. +Assumes file part is the same in both spans. +-} +combineRealSrcSpans :: RealSrcSpan -> RealSrcSpan -> RealSrcSpan +combineRealSrcSpans span1 span2 = RealSrcSpan'{..} + where + (_srcSpanSLine, _srcSpanSColumn) = + min + (span1 ^. srcSpanSLine, span1 ^. srcSpanSColumn) + (span2 ^. srcSpanSLine, span2 ^. srcSpanSColumn) + (_srcSpanELine, _srcSpanEColumn) = + max + (span1 ^. srcSpanELine, span1 ^. srcSpanEColumn) + (span2 ^. srcSpanELine, span2 ^. srcSpanEColumn) + _srcSpanFile = span1 ^. srcSpanFile + +{- | Combines two 'SrcSpan's into one that covers both original one. +Returns 'UnhelpfulSpan' if files differ. +-} +combineSrcSpans :: SrcSpan -> SrcSpan -> SrcSpan +combineSrcSpans (UnhelpfulSpan _) r = r +combineSrcSpans l (UnhelpfulSpan _) = l +combineSrcSpans (RealSrcSpan span1) (RealSrcSpan span2) + | span1 ^. srcSpanFile == span2 ^. srcSpanFile = RealSrcSpan $ combineRealSrcSpans span1 span2 + | otherwise = mkBadSrcSpan "" + +-- | 'SrcSpan's will be attached to nearly everything, let's create a type for attaching. +data GenLocated l e = L l e deriving (Show) + +{- | This is very specific instance for 'Eq'. +It doesn't compare locations, only contents. +-} +instance (Eq e) => Eq (GenLocated l e) where + (==) :: GenLocated l e -> GenLocated l e -> Bool + (L _ a) == (L _ b) = a == b + +makePrisms ''GenLocated + +type Located = GenLocated SrcSpan +type RealLocated = GenLocated RealSrcSpan + +mkBadLocated :: Text -> e -> Located e +mkBadLocated str = L (mkBadSrcSpan str) + +combineLocs :: Located a -> Located b -> SrcSpan +combineLocs (L loc1 _) (L loc2 _) = combineSrcSpans loc1 loc2 + +unLoc :: GenLocated l e -> e +unLoc (L _ e) = e + +getLoc :: GenLocated l e -> l +getLoc (L loc _) = loc diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/PrettyAst.hs b/lamagraph-compiler/src/Lamagraph/Compiler/PrettyAst.hs new file mode 100644 index 0000000..eaf5d7b --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/PrettyAst.hs @@ -0,0 +1,148 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +{- | Orphan instances for pretty-printing AST + +__Warning__: DO NOT import together with "Lamagraph.Compiler.PrettyLml"! +-} +module Lamagraph.Compiler.PrettyAst () where + +import Relude + +import Control.Lens +import Prettyprinter + +import Lamagraph.Compiler.Extension +import Lamagraph.Compiler.Parser.SrcLoc +import Lamagraph.Compiler.Syntax + +spaceNumber :: Int +spaceNumber = 1 + +-- Default instance for 'Pretty (Maybe a)' prints empty string for 'Nothing' +prettyMaybe :: (Pretty a) => Maybe a -> Doc ann +prettyMaybe Nothing = parens "Nothing" +prettyMaybe (Just a) = parens $ vsep ["Just", pretty a] + +instance Pretty RealSrcSpan where + pretty :: RealSrcSpan -> Doc ann + pretty rss = pretty _srcSpanFile <> ":" <> lineCol + where + _srcSpanFile = rss ^. srcSpanFile + _srcSpanSLine = rss ^. srcSpanSLine + _srcSpanSColumn = rss ^. srcSpanSColumn + _srcSpanELine = rss ^. srcSpanELine + _srcSpanEColumn = rss ^. srcSpanEColumn + lineCol = + if _srcSpanSLine == _srcSpanELine + then pretty _srcSpanSLine <> ":" <> pretty _srcSpanSColumn <> "-" <> pretty _srcSpanEColumn + else + parens (pretty _srcSpanSLine <> "," <> pretty _srcSpanSColumn) + <> "-" + <> parens (pretty _srcSpanELine <> "," <> pretty _srcSpanEColumn) + +instance Pretty UnhelpfulSpanReason where + pretty :: UnhelpfulSpanReason -> Doc ann + pretty UnhelpfulGenerated = "" + pretty (UnhelpfulOther text) = angles $ pretty text + +instance Pretty SrcSpan where + pretty :: SrcSpan -> Doc ann + pretty ss = braces (space <> inner <> space) + where + inner = case ss of + RealSrcSpan rss -> pretty rss + UnhelpfulSpan usr -> pretty usr + +instance (Pretty a) => Pretty (Located a) where + pretty :: Located a -> Doc ann + pretty (L loc a) = parens (align inner) + where + inner = vsep ["L", pretty loc, parens $ nest spaceNumber $ pretty a] + +instance {-# OVERLAPPING #-} Pretty (Located Text) where + pretty :: Located Text -> Doc ann + pretty (L loc text) = parens (align inner) + where + inner = vsep ["L", pretty loc, dquotes $ pretty text] + +instance Pretty RecFlag where + pretty :: RecFlag -> Doc ann + pretty Recursive = "Rec" + pretty NonRecursive = "NonRec" + +instance Pretty (LmlDecl (LmlcPass pass)) where + pretty :: LmlDecl (LmlcPass pass) -> Doc ann + pretty (OpenD _ decl) = vsep ["OpenD", pretty decl] + pretty (ValD _ recFlag binds) = vsep ["ValD" <+> pretty recFlag, list $ map pretty (toList binds)] + pretty (TyD _ decls) = vsep ["TyD", list $ map pretty (toList decls)] + +instance Pretty (OpenDecl (LmlcPass pass)) where + pretty :: OpenDecl (LmlcPass pass) -> Doc ann + pretty (OpenDecl _ ident) = vsep ["OpenDecl", pretty ident] + +instance Pretty (TyDecl (LmlcPass pass)) where + pretty :: TyDecl (LmlcPass pass) -> Doc ann + pretty (AliasDecl _ name vars ty) = vsep ["AliasDecl", pretty name, list $ map pretty vars, pretty ty] + pretty (DataDecl _ name vars constrs) = vsep ["DataDecl", pretty name, list $ map pretty vars, list $ map pretty constrs] + +instance Pretty (ConDecl (LmlcPass pass)) where + pretty :: ConDecl (LmlcPass pass) -> Doc ann + pretty (ConDecl _ name args) = vsep ["ConDecl", pretty name, list (map pretty args)] + +instance Pretty (LmlExpr (LmlcPass pass)) where + pretty :: LmlExpr (LmlcPass pass) -> Doc ann + pretty (LmlExprIdent _ ident) = vsep ["ExprIdent", pretty ident] + pretty (LmlExprConstant _ constant) = "ExprConstant" <+> pretty constant + pretty (LmlExprLet _ recFlag binds expr) = vsep ["ExprLet" <+> pretty recFlag, list $ map pretty (toList binds), pretty expr] + pretty (LmlExprFunction _ pat expr) = vsep ["ExprFunction", pretty pat, pretty expr] + pretty (LmlExprApply _ expr exprs) = vsep ["ExprApply", pretty expr, list $ map pretty (toList exprs)] + pretty (LmlExprMatch _ expr cases) = vsep ["ExprMatch", pretty expr, list $ map pretty (toList cases)] + pretty (LmlExprTuple _ expr exprs) = vsep ["ExprTuple", list $ map pretty (expr : toList exprs)] + pretty (LmlExprConstruct _ constr expr) = vsep ["ExprConstruct", pretty constr, prettyMaybe expr] + pretty (LmlExprIfThenElse _ cond t f) = vsep ["ExprITE", pretty cond, pretty t, pretty f] + pretty (LmlExprConstraint _ expr ty) = vsep ["ExprConstraint", pretty expr, pretty ty] + +instance Pretty (LmlBind (LmlcPass pass)) where + pretty :: LmlBind (LmlcPass pass) -> Doc ann + pretty (LmlBind _ pat expr) = vsep ["Bind", pretty pat, pretty expr] + +instance Pretty (LmlCase (LmlcPass pass)) where + pretty :: LmlCase (LmlcPass pass) -> Doc ann + pretty (LmlCase _ pat constraint expr) = vsep ["Case", pretty pat, prettyMaybe constraint, pretty expr] + +instance Pretty (LmlLit (LmlcPass pass)) where + pretty :: LmlLit (LmlcPass pass) -> Doc ann + pretty (LmlInt _ int) = pretty int + pretty (LmlInt32 _ int32) = pretty int32 <> "l" + pretty (LmlUInt32 _ uint32) = pretty uint32 <> "ul" + pretty (LmlInt64 _ int64) = pretty int64 <> "L" + pretty (LmlUInt64 _ uint64) = pretty uint64 <> "UL" + pretty (LmlChar _ char) = squotes $ pretty char + pretty (LmlString _ str) = dquotes $ pretty str + +instance Pretty Longident where + pretty :: Longident -> Doc ann + pretty (Longident idents) = dquotes $ hsep $ punctuate comma (map pretty (toList idents)) + +instance Pretty (LmlPat (LmlcPass pass)) where + pretty :: LmlPat (LmlcPass pass) -> Doc ann + pretty (LmlPatAny _) = "PatAny" + pretty (LmlPatVar _ var) = vsep ["PatVar", pretty var] + pretty (LmlPatConstant _ constant) = vsep ["PatConstant", pretty constant] + pretty (LmlPatTuple _ pat pats) = vsep ["PatTuple", list $ map pretty (pat : toList pats)] + pretty (LmlPatConstruct _ constr pat) = vsep ["PatConstruct", pretty constr, prettyMaybe pat] + pretty (LmlPatOr _ pat1 pat2) = vsep ["ParOr", pretty pat1, pretty pat2] + pretty (LmlPatConstraint _ pat ty) = vsep ["PatConstraint", pretty pat, pretty ty] + +instance Pretty (LmlType (LmlcPass pass)) where + pretty :: LmlType (LmlcPass pass) -> Doc ann + pretty (LmlTyVar _ var) = vsep ["TyVar", pretty var] + pretty (LmlTyArrow _ ty1 ty2) = vsep ["TyArrow", pretty ty1, pretty ty2] + pretty (LmlTyTuple _ ty tys) = vsep ["TyTuple", list $ map pretty (ty : toList tys)] + pretty (LmlTyConstr _ constr tys) = vsep ["TyConstr", pretty constr, list (map pretty tys)] + +instance Pretty (LmlModule (LmlcPass pass)) where + pretty :: LmlModule (LmlcPass pass) -> Doc ann + pretty (LmlModule _ name decls) = parens $ nest spaceNumber inner + where + inner = vsep ["Module", prettyMaybe name, list $ map pretty decls] diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/PrettyLml.hs b/lamagraph-compiler/src/Lamagraph/Compiler/PrettyLml.hs new file mode 100644 index 0000000..9e27ea0 --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/PrettyLml.hs @@ -0,0 +1,160 @@ +{-# LANGUAGE QuasiQuotes #-} +{-# OPTIONS_GHC -Wno-orphans #-} + +{- | Orphan instances for pretty-printing LamagraphML + +__Warning__: DO NOT import together with "Lamagraph.Compiler.PrettyAst"! +-} +module Lamagraph.Compiler.PrettyLml () where + +import Relude + +import Data.String.Interpolate (i) +import Data.Text qualified as T +import Prettyprinter + +import Lamagraph.Compiler.Extension +import Lamagraph.Compiler.Parser.SrcLoc +import Lamagraph.Compiler.Syntax + +prettyADTVar :: LLmlType (LmlcPass pass) -> Doc ann +prettyADTVar (L _ (LmlTyVar _ varName)) = "'" <> pretty varName +prettyADTVar t = + error + [i|Internal pretty-printer error: expected list of type variables in AST as a type parameters, but got #{pretty t}|] + +prettyADTVars :: [LLmlType (LmlcPass pass)] -> Doc ann +prettyADTVars [] = emptyDoc +prettyADTVars [var] = prettyADTVar var <> space +prettyADTVars vars = parens (fillSep $ punctuate comma (map prettyADTVar vars)) <> space + +prettyChar :: Char -> Doc ann +prettyChar '\\' = "\\\\" +prettyChar '\"' = "\\\"" +prettyChar '\'' = "\\\'" +prettyChar '\n' = "\\\n" +prettyChar c + | c `elem` ['\32' .. '\127'] = pretty c + | otherwise = error [i|Internal pretty-printer error: trying to print unsupported in LML character "#{c}"|] + +prettyString :: Text -> Doc ann +prettyString str = mconcat $ map prettyChar (toString str) + +instance (Pretty a) => Pretty (Located a) where + pretty :: Located a -> Doc ann + pretty (L _ a) = pretty a + +instance Pretty RecFlag where + pretty :: RecFlag -> Doc ann + pretty Recursive = "rec" <> space + pretty NonRecursive = emptyDoc + +instance Pretty (LmlDecl (LmlcPass pass)) where + pretty :: LmlDecl (LmlcPass pass) -> Doc ann + pretty (OpenD _ decl) = "open" <+> pretty decl + pretty (ValD _ recFlag binds) = "let" <+> pretty recFlag <> concatWith (surround (hardline <> "and" <> hardline)) (map pretty (toList binds)) + pretty (TyD _ decls) = "type" <+> concatWith (surround (hardline <> "and" <> hardline)) (map pretty (toList decls)) + +instance Pretty (OpenDecl (LmlcPass pass)) where + pretty :: OpenDecl (LmlcPass pass) -> Doc ann + pretty (OpenDecl _ ident) = pretty ident + +instance Pretty (TyDecl (LmlcPass pass)) where + pretty :: TyDecl (LmlcPass pass) -> Doc ann + pretty (AliasDecl _ name vars ty) = prettyADTVars vars <> pretty name <+> "=" <> softline <> pretty ty + pretty (DataDecl _ name vars []) = prettyADTVars vars <> pretty name + pretty (DataDecl _ name vars constrs) = + prettyADTVars vars + <> pretty name + <+> align ("=" <+> encloseSep emptyDoc emptyDoc (flatAlt "| " " | ") (map pretty constrs)) + +instance Pretty (ConDecl (LmlcPass pass)) where + pretty :: ConDecl (LmlcPass pass) -> Doc ann + pretty (ConDecl _ (L _ "::") args) = "(::)" <+> "of" <+> concatWith (surround " * ") (map pretty args) + pretty (ConDecl _ name args) = + pretty name <> case args of + [] -> emptyDoc + _ -> space <> "of" <+> concatWith (surround " * ") (map pretty args) + +instance Pretty (LmlExpr (LmlcPass pass)) where + pretty :: LmlExpr (LmlcPass pass) -> Doc ann + pretty (LmlExprIdent _ ident) = pretty ident + pretty (LmlExprConstant _ constant) = pretty constant + pretty (LmlExprLet _ recFlag binds expr) = + "let" + <+> pretty recFlag + <> concatWith (surround (hardline <> "and" <> hardline)) (map pretty (toList binds)) + <+> "in" + <+> pretty expr + pretty (LmlExprFunction _ pat expr) = "fun" <+> pretty pat <+> "->" <+> pretty expr + pretty (LmlExprApply _ expr exprs) = pretty expr <+> hsep (map (parens . pretty) (toList exprs)) + pretty (LmlExprMatch _ expr cases) = + parens $ + align + ("match" <+> pretty expr <+> "with" <+> encloseSep emptyDoc emptyDoc (flatAlt "| " " | ") (map pretty (toList cases))) + pretty (LmlExprTuple _ expr exprs) = parens (fillSep $ punctuate comma (map (parens . pretty) (expr : toList exprs))) + pretty (LmlExprConstruct _ (L _ (Longident ("::" :| []))) (Just (L _ (LmlExprTuple _ hd tl)))) = parens (pretty hd <+> "::" <+> pretty (head tl)) + pretty (LmlExprConstruct _ constr Nothing) = pretty constr + pretty (LmlExprConstruct _ constr (Just expr)) = parens (pretty constr <+> parens (pretty expr)) + pretty (LmlExprIfThenElse _ cond t f) = "if" <+> pretty cond <+> "then" <+> pretty t <+> "else" <+> pretty f + pretty (LmlExprConstraint _ expr ty) = parens (pretty expr <+> ":" <+> pretty ty) + +instance Pretty (LmlBind (LmlcPass pass)) where + pretty :: LmlBind (LmlcPass pass) -> Doc ann + pretty (LmlBind _ pat expr) = pretty pat <+> "=" <> softline <> pretty expr + +instance Pretty (LmlCase (LmlcPass pass)) where + pretty :: LmlCase (LmlcPass pass) -> Doc ann + pretty (LmlCase _ pat Nothing expr) = pretty pat <+> "->" <+> pretty expr + pretty (LmlCase _ pat (Just constraint) expr) = pretty pat <+> "when" <+> pretty constraint <+> "->" <+> pretty expr + +instance Pretty (LmlLit (LmlcPass pass)) where + pretty :: LmlLit (LmlcPass pass) -> Doc ann + pretty (LmlInt _ int) = pretty int + pretty (LmlInt32 _ int32) = pretty int32 <> "l" + pretty (LmlUInt32 _ uint32) = pretty uint32 <> "ul" + pretty (LmlInt64 _ int64) = pretty int64 <> "L" + pretty (LmlUInt64 _ uint64) = pretty uint64 <> "UL" + pretty (LmlChar _ char) = squotes $ prettyChar char + pretty (LmlString _ str) = dquotes $ prettyString str + +instance Pretty Longident where + pretty :: Longident -> Doc ann + pretty (Longident ident) = if res then prettyInit <> parens (space <> pretty func <> space) else prettyInit <> pretty func + where + func = last ident + initList = init ident + prettyInit = if null initList then emptyDoc else concatWith (surround dot) (map pretty initList) <> dot + prefixes = ["*", "/", "%", "+", "-", "@", "^", "=", "<", ">", "|", "&", "$", "!", "?", "~"] + equal = ["lor", "lxor", "mod", "land", "lsl", "lsr", "asr"] + startsWithB = map (`T.isPrefixOf` func) prefixes + equalsB = map (func ==) equal + res = or startsWithB || or equalsB + +instance Pretty (LmlPat (LmlcPass pass)) where + pretty :: LmlPat (LmlcPass pass) -> Doc ann + pretty (LmlPatAny _) = "_" + pretty (LmlPatVar _ (L _ var)) = pretty $ mkLongident (pure var) + pretty (LmlPatConstant _ constant) = pretty constant + pretty (LmlPatTuple _ pat pats) = parens $ fillSep (punctuate comma (map pretty (pat : toList pats))) + pretty (LmlPatConstruct _ (L _ (Longident ("::" :| []))) (Just (L _ (LmlPatTuple _ hd tl)))) = parens (pretty hd <+> "::" <+> pretty (head tl)) + pretty (LmlPatConstruct _ constr Nothing) = pretty constr + pretty (LmlPatConstruct _ constr (Just pat)) = parens (pretty constr <+> parens (pretty pat)) + pretty (LmlPatOr _ pat1 pat2) = parens (pretty pat1 <+> "|" <+> pretty pat2) + pretty (LmlPatConstraint _ pat ty) = parens (pretty pat <+> ":" <+> pretty ty) + +instance Pretty (LmlType (LmlcPass pass)) where + pretty :: LmlType (LmlcPass pass) -> Doc ann + pretty (LmlTyVar _ var) = "'" <> pretty var + pretty (LmlTyArrow _ ty1 ty2) = parens (pretty ty1 <+> "->" <> softline <> pretty ty2) + pretty (LmlTyTuple _ ty tys) = parens $ concatWith (surround " * ") (map pretty (ty : toList tys)) + pretty (LmlTyConstr _ constr []) = pretty constr + pretty (LmlTyConstr _ constr [ty@(L _ (LmlTyArrow{}))]) = parens (pretty ty) <+> pretty constr + pretty (LmlTyConstr _ constr [ty]) = pretty ty <+> pretty constr + pretty (LmlTyConstr _ constr tys) = parens (fillSep $ punctuate comma (map pretty tys)) <+> pretty constr + +instance Pretty (LmlModule (LmlcPass pass)) where + pretty :: LmlModule (LmlcPass pass) -> Doc ann + pretty (LmlModule _ name decls) = concatWith (surround (hardline <> hardline)) $ case name of + Nothing -> map pretty decls + Just moduleName -> ("module" <+> pretty moduleName) : map pretty decls diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Syntax.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax.hs index a969e23..d0bba55 100644 --- a/lamagraph-compiler/src/Lamagraph/Compiler/Syntax.hs +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax.hs @@ -1,62 +1,93 @@ +{-# LANGUAGE UndecidableInstances #-} + {- | -LamagraphML syntax +LamagraphML syntax description and top-level module type + +For more information on design see "Lamagraph.Compiler.Syntax.Extension". -} module Lamagraph.Compiler.Syntax ( - -- * Grammar rules + -- * Language description + + -- ** Grammar rules -- $rules - -- * Lexical conventions + -- ** Lexical conventions - -- ** Blanks + -- *** Blanks -- $lexing_blanks - -- ** Comments + -- *** Comments -- $lexing_comments - -- ** Identifiers + -- *** Identifiers -- $lexing_idents - -- ** Integer literals + -- *** Integer literals -- $lexing_int_lits - -- ** Character literals + -- *** Character literals -- $lexing_char_lits - -- ** String literals + -- *** String literals -- $lexing_string_lits - -- ** Operators + -- *** Operators -- $lexing_operators - -- ** Keywords + -- *** Keywords -- $lexing_keywords - -- * Names + -- ** Names -- $names - -- * Type expressions + -- ** Type expressions -- $types - -- * Constants + -- ** Constants -- $constants - -- * Patterns + -- ** Patterns -- $patterns - -- * Expressions + -- ** Expressions -- $expressions - -- * Type definitions + -- ** Type definitions -- $typedefs - -- * Declarations and Modules + -- ** Declarations and Modules -- $decls - -- * Missing things + -- ** Missing things -- $missing + + -- * AST types + + -- ** LmlModule type + LmlModule (..), + ForallLmlModule, + + -- ** Reexports + module Lamagraph.Compiler.Syntax.Decl, + module Lamagraph.Compiler.Syntax.Expr, + module Lamagraph.Compiler.Syntax.Extension, + module Lamagraph.Compiler.Syntax.Lit, + module Lamagraph.Compiler.Syntax.Longident, + module Lamagraph.Compiler.Syntax.Pat, + module Lamagraph.Compiler.Syntax.Type, ) where +import Relude + +import Lamagraph.Compiler.Syntax.Decl +import Lamagraph.Compiler.Syntax.Expr +import Lamagraph.Compiler.Syntax.Extension +import Lamagraph.Compiler.Syntax.Lit +import Lamagraph.Compiler.Syntax.Longident +import Lamagraph.Compiler.Syntax.Pat +import Lamagraph.Compiler.Syntax.Type + {- $rules Grammar rules are written in monospace font. Italicized words represent @/nonterminals/@. @@ -82,11 +113,13 @@ Note that the following code won't be treated as a valid multiline comment! -- (* *) @ + +/Note:/ now we can't have @~-@ unary minus, thus @--@ is a subject for change. -} {- $lexing_idents @ -/ident/ ::= /letter/ { /letter/ | 0...9 | _ | ' } +/ident/ ::= ( /letter/ | _ ) { /letter/ | 0...9 | _ | ' } /capitalized-ident/ ::= ( A...Z ) { /letter/ | 0...9 | _ | ' } @@ -94,6 +127,8 @@ Note that the following code won't be treated as a valid multiline comment! /letter/ ::= A...Z | a...z @ + +@/ident/@ can be written as @( /capitalized-ident/ | /lowercase-ident/ )@. -} {- $lexing_int_lits @@ -108,6 +143,8 @@ Note that the following code won't be treated as a valid multiline comment! /uint64-literal/ ::= /integer-literal/ UL @ + +Values outside of type range will overflow. -} {- $lexing_char_lits @@ -115,10 +152,10 @@ Note that the following code won't be treated as a valid multiline comment! /char-literal/ ::= ' /regular-char/ ' | ' /escape-sequence/ ' -/escape-sequence/ ::= \\ ( __|__ | " | ' | n ) +/escape-sequence/ ::= \\ ( \\ | " | ' | n ) @ -@/regular-char/@ must match every printable ASCII character (decimal range: 32-126). +@/regular-char/@ must match every printable ASCII character (decimal range: 32-126 excluding escaped characters). -} {- $lexing_string_lits @@ -129,7 +166,7 @@ Note that the following code won't be treated as a valid multiline comment! | /escape-sequence/ @ -@/regular-string-character/@ must match every printable ASCII character (decimal range: 32-126). +@/regular-string-character/@ must match every printable ASCII character (decimal range: 32-126 excluding escaped characters). -} {- $lexing_operators @@ -142,8 +179,6 @@ Note that the following code won't be treated as a valid multiline comment! /operator-char/ ::= ! | $ | % | & | * | + | . | / | : | \< | = | \> | ? | \@ | ^ | __|__ | ~ @ - -Copypasted from https://askra.de/software/ocaml-doc/4.02/lex.html#sec71, probably too complicated. -} {- $lexing_keywords @@ -154,8 +189,7 @@ and asr else false fun if in land let lor lsl lsr lxor match mod module of open rec then true type when with -!= && ' ( ) * + , - -> : :: ; < = > [ ] -_ { | } . +&& ' ( ) * + , - -> : :: ; = [ ] _ . | || @ -} @@ -168,8 +202,7 @@ Basic names /operator-name/ ::= /prefix-symbol/ | /infix-op/ -/infix-op/ ::= /infix-symbol/ - | * | + | - | = | != | \< | \> | || | && +/infix-op/ ::= /infix-symbol/ | * | + | - | = | || | && | mod | land | lor | lxor | lsl | lsr | asr /constr-name/ ::= /capitalized-ident/ @@ -230,7 +263,7 @@ Qualified names | __(__ /pattern/ : /typexpr/ __)__ | /pattern/ __|__ /pattern/ | /constr/ /pattern/ - | /pattern/ { , /pattern/ } + | /pattern/ { , /pattern/ }+ | __[__ /pattern/ { ; /pattern/ } [;] __]__ | /pattern/ :: /pattern/ @ @@ -243,7 +276,7 @@ Qualified names | /constant/ | __(__ /expr/ __)__ | __(__ /expr/ : /typexpr/ __)__ - | /expr/ {, /expr/ } + | /expr/ {, /expr/ }+ | /constr/ /expr/ | /expr/ :: /expr/ | __[__ /expr/ { ; /expr/ } [;] ] @@ -272,14 +305,13 @@ Qualified names @ /type-definition/ ::= type /typedef/ { and /typedef/ } -/typedef/ ::= [ /type-params/ ] /typeconstr-name/ /type-information/ +/typedef/ ::= [ /type-params/ ] /typeconstr-name/ [ /type-information/ ] -/type-information/ ::= [ /type-equation/ ] [ /type-representation/ ] +/type-information/ ::= /type-equation/ | /type-representation/ /type-equation/ ::= = /typexpr/ /type-representation/ ::= = [ __|__ ] /constr-decl/ { __|__ /constr-decl/ } - | = __|__ /type-params/ ::= /type-param/ | __(__ /type-param/ {, /type-param/ } __)__ @@ -295,13 +327,13 @@ Qualified names {- $decls @ -/module-definition/ ::= module /module-path/ - -/open-decl/ ::= open /module-path/ +/module-expression/ ::= [ /module-definition/ ] { /module-item/ } -/decl/ ::= /expr/ | /type-definition/ | /open-decl/ +/module-definition/ ::= module /module-path/ -/prog/ ::= [ /module-definition/ ] { /decl/ } +/module-item/ ::= let [rec] /let-binding/ { and /let-binding/ } + | /type-definition/ + | open /module-path/ @ -} @@ -315,3 +347,21 @@ For the sake of simplicity this language currently lacks these know to the autho * Float numbers * @function@ keyword -} + +-- | LamagraphML module +data LmlModule pass + = LmlModule + { _lmlModExt :: XCModule pass + -- ^ LmlModule extension point + , _lmlModName :: Maybe (LLongident pass) + -- ^ 'Nothing' if "@module X@" is omitted. + , _lmlModDecls :: [LLmlDecl pass] + -- ^ Open, type and let declarations + } + | XModule !(XXModule pass) + +type ForallLmlModule (tc :: Type -> Constraint) pass = + (tc (XCModule pass), tc (LLongident pass), tc (LLmlDecl pass), tc (XXModule pass)) + +deriving instance (ForallLmlModule Show pass) => Show (LmlModule pass) +deriving instance (ForallLmlModule Eq pass) => Eq (LmlModule pass) diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Decl.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Decl.hs new file mode 100644 index 0000000..8715eec --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Decl.hs @@ -0,0 +1,108 @@ +{-# LANGUAGE UndecidableInstances #-} + +-- | LamagraphML declarations +module Lamagraph.Compiler.Syntax.Decl ( + LLmlDecl, + LmlDecl (..), + ForallLmlDecl, + LOpenDecl, + OpenDecl (..), + ForallOpenDecl, + LTyDecl, + TyDecl (..), + ForallTyDecl, + LConDecl, + ConDecl (..), + ForallConDecl, +) where + +import Relude + +import Lamagraph.Compiler.Syntax.Expr +import Lamagraph.Compiler.Syntax.Extension +import Lamagraph.Compiler.Syntax.Longident +import Lamagraph.Compiler.Syntax.Type + +-- | Located 'LmlDecl' +type LLmlDecl pass = XLocated pass (LmlDecl pass) + +-- | A LamagraphML declaration +data LmlDecl pass + = OpenD (XOpenD pass) (OpenDecl pass) + | ValD (XValD pass) RecFlag (NonEmpty (LLmlBind pass)) + | TyD (XTyD pass) (NonEmpty (LTyDecl pass)) + | XLmlDecl !(XXDecl pass) + +type ForallLmlDecl (tc :: Type -> Constraint) pass = + ( tc (XOpenD pass) + , tc (OpenDecl pass) + , tc (XValD pass) + , tc (LLmlBind pass) + , tc (XTyD pass) + , tc (LTyDecl pass) + , tc (XXDecl pass) + ) + +deriving instance (ForallLmlDecl Show pass) => Show (LmlDecl pass) +deriving instance (ForallLmlDecl Eq pass) => Eq (LmlDecl pass) + +-- | Located open declaration +type LOpenDecl pass = XLocated pass (OpenDecl pass) + +{- | Open declaration + +Too small to have separate module. +-} +data OpenDecl pass + = OpenDecl (XOpenDecl pass) (LLongident pass) + | XOpenDecl !(XXOpenDecl pass) + +type ForallOpenDecl (tc :: Type -> Constraint) pass = + (tc (XOpenDecl pass), tc (XXOpenDecl pass), tc (LLongident pass)) + +deriving instance (ForallOpenDecl Show pass) => Show (OpenDecl pass) +deriving instance (ForallOpenDecl Eq pass) => Eq (OpenDecl pass) + +-- | Located type declaration +type LTyDecl pass = XLocated pass (TyDecl pass) + +-- | Type declaration +data TyDecl pass + = -- | Type alias from @type name = typexpr@ + AliasDecl (XAliasDecl pass) (XLocated pass Text) [LLmlType pass] (LLmlType pass) + | -- | ADT declaration from @type 'a name = C1 of 'a * 'a type | ...@. + DataDecl (XDataDecl pass) (XLocated pass Text) [LLmlType pass] [LConDecl pass] + | XTyDecl !(XXTyDecl pass) + +type ForallTyDecl (tc :: Type -> Constraint) pass = + ( tc (XAliasDecl pass) + , tc (XLocated pass Text) + , tc (LLmlType pass) + , tc (XDataDecl pass) + , tc (LConDecl pass) + , tc (XXTyDecl pass) + ) + +deriving instance (ForallTyDecl Show pass) => Show (TyDecl pass) +deriving instance (ForallTyDecl Eq pass) => Eq (TyDecl pass) + +-- | Located ADT constructor declaration +type LConDecl pass = XLocated pass (ConDecl pass) + +-- | ADT constructor declaration +data ConDecl pass + = ConDecl + { _cdExt :: XConDecl pass + -- ^ ConDecl extension point + , _cdConName :: XLocated pass Text + -- ^ Constructor name + , _cdArgs :: [LLmlType pass] + -- ^ Constructor type arguments + } + | XConDecl !(XXConDecl pass) + +type ForallConDecl (tc :: Type -> Constraint) pass = + (tc (XConDecl pass), tc (XLocated pass Text), tc (LLmlType pass), tc (XXConDecl pass)) + +deriving instance (ForallConDecl Show pass) => Show (ConDecl pass) +deriving instance (ForallConDecl Eq pass) => Eq (ConDecl pass) diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Expr.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Expr.hs new file mode 100644 index 0000000..5d30cac --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Expr.hs @@ -0,0 +1,115 @@ +{-# LANGUAGE UndecidableInstances #-} + +-- | LamagraphML expressions +module Lamagraph.Compiler.Syntax.Expr ( + RecFlag (..), + LLmlExpr, + LmlExpr (..), + ForallLmlExpr, + LLmlBind, + LmlBind (..), + ForallLmlBind, + LLmlCase, + LmlCase (..), + ForallLmlCase, +) where + +import Relude + +import Lamagraph.Compiler.Syntax.Extension +import Lamagraph.Compiler.Syntax.Lit +import Lamagraph.Compiler.Syntax.Longident +import Lamagraph.Compiler.Syntax.Pat +import Lamagraph.Compiler.Syntax.Type + +-- | Flag for recursive let-bindings +data RecFlag = Recursive | NonRecursive deriving (Show, Eq) + +-- | Located 'LmlExpr' +type LLmlExpr pass = XLocated pass (LmlExpr pass) + +-- | LamagraphML expression +data LmlExpr pass + = -- | Represents identifier, e.g. @A.B.C.fun@ + LmlExprIdent (XLmlExprIdent pass) Longident + | -- | Represents constant value like @-1@ or @"text"@ + LmlExprConstant (XLmlExprConstant pass) (LmlLit pass) + | -- | Represents let-binding + LmlExprLet (XLmlExprLet pass) RecFlag (NonEmpty (LLmlBind pass)) (LLmlExpr pass) + | -- | Represents @fun x -> expr@ construction + LmlExprFunction (XLmlExprFunction pass) (LLmlPat pass) (LLmlExpr pass) + | -- | Function application @f x y z@ + LmlExprApply (XLmlExprApply pass) (LLmlExpr pass) (NonEmpty (LLmlExpr pass)) + | -- | Match expression + -- + -- @ + -- match x with + -- | pat -> expr + -- @ + LmlExprMatch (XLmlExprMatch pass) (LLmlExpr pass) (NonEmpty (LLmlCase pass)) + | -- | Tuple representation, invariant \(n \geq 2\) + LmlExprTuple (XLmlExprTuple pass) (LLmlExpr pass) (NonEmpty (LLmlExpr pass)) + | -- | Constructor application + -- + -- Constructors aren't curried, this means that they must be applied to a tuple. + LmlExprConstruct (XLmlExprConstruct pass) (LLongident pass) (Maybe (LLmlExpr pass)) + | -- | Represents @if expr then expr else expr@ + LmlExprIfThenElse (XLmlExprIfThenElse pass) (LLmlExpr pass) (LLmlExpr pass) (LLmlExpr pass) + | -- | Represents expression constrained by type as in @(expr : typexpr)@ + LmlExprConstraint (XLmlExprConstraint pass) (LLmlExpr pass) (LLmlType pass) + | XLmlExpr !(XXExpr pass) + +type ForallLmlExpr (tc :: Type -> Constraint) pass = + ( tc (XLmlExprIdent pass) + , tc (XLmlExprConstant pass) + , tc (LmlLit pass) + , tc (XLmlExprLet pass) + , tc (LLmlBind pass) + , tc (LLmlExpr pass) + , tc (XLmlExprFunction pass) + , tc (LLmlPat pass) + , tc (XLmlExprApply pass) + , tc (XLmlExprMatch pass) + , tc (LLmlCase pass) + , tc (XLmlExprTuple pass) + , tc (XLmlExprConstruct pass) + , tc (LLongident pass) + , tc (XLmlExprIfThenElse pass) + , tc (XLmlExprConstraint pass) + , tc (LLmlType pass) + , tc (XXExpr pass) + ) + +deriving instance (ForallLmlExpr Show pass) => Show (LmlExpr pass) +deriving instance (ForallLmlExpr Eq pass) => Eq (LmlExpr pass) + +-- | Located let binder +type LLmlBind pass = XLocated pass (LmlBind pass) + +{- | Let binder type. + +Resides here due to size and to save us from .hs-boot file. +-} +data LmlBind pass + = LmlBind (XLmlBind pass) (LLmlPat pass) (LLmlExpr pass) + | XLmlBind !(XXBind pass) + +type ForallLmlBind (tc :: Type -> Constraint) pass = + (tc (XLmlBind pass), tc (LLmlPat pass), tc (LLmlExpr pass), tc (XXBind pass)) + +deriving instance (ForallLmlBind Show pass) => Show (LmlBind pass) +deriving instance (ForallLmlBind Eq pass) => Eq (LmlBind pass) + +-- | Located case binder type +type LLmlCase pass = XLocated pass (LmlCase pass) + +-- | Case binder type +data LmlCase pass + = LmlCase (XLmlCase pass) (LLmlPat pass) (Maybe (LLmlExpr pass)) (LLmlExpr pass) + | XLmlCase !(XXCase pass) + +type ForallLmlCase (tc :: Type -> Constraint) pass = + (tc (XLmlCase pass), tc (LLmlPat pass), tc (LLmlExpr pass), tc (XXCase pass)) + +deriving instance (ForallLmlCase Show pass) => Show (LmlCase pass) +deriving instance (ForallLmlCase Eq pass) => Eq (LmlCase pass) diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Extension.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Extension.hs new file mode 100644 index 0000000..cd8ccbc --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Extension.hs @@ -0,0 +1,144 @@ +{-# LANGUAGE TypeFamilyDependencies #-} +-- Exporting every type family from here will be too tedious +{-# OPTIONS_GHC -Wno-missing-export-lists #-} + +{- | Module with TTG extension points type families + +AST here is designed using Trees That Grow () pattern. +It uses type families to contain phase-specific information. +Simpler, but more up-to-date explanations can be found here: + +* +* +* + +Regarding directory structure, it loosely follows GHC's one. +In "Lamagraph.Compiler.Syntax".* we have the most general tree with open type families, +meaning that "Lamagraph.Compiler.Syntax".* can easily be transformed into a library. +All the specialization must be done outside (currently in "Lamagraph.Compiler.Extension"). +-} +module Lamagraph.Compiler.Syntax.Extension where + +import Relude + +{- | Type to serve as a placeholder for TTG extension points, which can be constructed, +but aren't used to hold something more useful. +-} +data NoExtField = NoExtField deriving (Show, Eq) + +-- | Is used to construct a term. +noExtField :: NoExtField +noExtField = NoExtField + +-- | Isomorphic to 'Void'. +data DataConCantHappen deriving (Show, Eq) + +dataConCanHappen :: DataConCantHappen -> a +dataConCanHappen x = case x of {} + +-- | Used to add location to the tree +type family XLocated p a = r | r -> a + +-------------------------------------------------- +-- Type families for LmlModule extension points -- +-------------------------------------------------- + +type family XCModule x +type family XXModule x + +------------------------------------------------ +-- Type families for LmlDecl extension points -- +------------------------------------------------ + +type family XOpenD x +type family XValD x +type family XTyD x +type family XXDecl x + +------------------------------------------------- +-- Type families for OpenDecl extension points -- +------------------------------------------------- + +type family XOpenDecl x +type family XXOpenDecl x + +----------------------------------------------- +-- Type families for TyDecl extension points -- +----------------------------------------------- + +type family XAliasDecl x +type family XDataDecl x +type family XXTyDecl x + +------------------------------------------------ +-- Type families for ConDecl extension points -- +------------------------------------------------ + +type family XConDecl x +type family XXConDecl x + +------------------------------------------------ +-- Type families for LmlType extension points -- +------------------------------------------------ + +type family XLmlTyVar x +type family XLmlTyArrow x +type family XLmlTyTuple x +type family XLmlTyConstr x +type family XXType x + +----------------------------------------------- +-- Type families for LmlLit extension points -- +----------------------------------------------- + +type family XLmlInt x +type family XLmlInt32 x +type family XLmlUInt32 x +type family XLmlInt64 x +type family XLmlUInt64 x +type family XLmlChar x +type family XLmlString x +type family XXLit x + +----------------------------------------------- +-- Type families for LmlPat extension points -- +----------------------------------------------- + +type family XLmlPatAny x +type family XLmlPatVar x +type family XLmlPatConstant x +type family XLmlPatTuple x +type family XLmlPatConstruct x +type family XLmlPatOr x +type family XLmlPatConstraint x +type family XXPat x + +------------------------------------------------ +-- Type families for LmlExpr extension points -- +------------------------------------------------ + +type family XLmlExprIdent x +type family XLmlExprConstant x +type family XLmlExprLet x +type family XLmlExprFunction x +type family XLmlExprApply x +type family XLmlExprMatch x +type family XLmlExprTuple x +type family XLmlExprConstruct x +type family XLmlExprIfThenElse x +type family XLmlExprConstraint x +type family XXExpr x + +------------------------------------------------ +-- Type families for LmlBind extension points -- +------------------------------------------------ + +type family XLmlBind x +type family XXBind x + +------------------------------------------------ +-- Type families for LmlCase extension points -- +------------------------------------------------ + +type family XLmlCase x +type family XXCase x diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Lit.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Lit.hs new file mode 100644 index 0000000..710b7b8 --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Lit.hs @@ -0,0 +1,33 @@ +{-# LANGUAGE UndecidableInstances #-} + +-- | LamagraphML literals +module Lamagraph.Compiler.Syntax.Lit (LmlLit (..), ForallLmlLit) where + +import Relude + +import Lamagraph.Compiler.Syntax.Extension + +-- | LamagraphML literal +data LmlLit pass + = LmlInt (XLmlInt pass) Int + | LmlInt32 (XLmlInt32 pass) Int32 + | LmlUInt32 (XLmlUInt32 pass) Word32 + | LmlInt64 (XLmlInt64 pass) Int64 + | LmlUInt64 (XLmlUInt64 pass) Word64 + | LmlChar (XLmlChar pass) Char + | LmlString (XLmlString pass) Text + | XLmlLit !(XXLit pass) + +type ForallLmlLit (tc :: Type -> Constraint) pass = + ( tc (XLmlInt pass) + , tc (XLmlInt32 pass) + , tc (XLmlUInt32 pass) + , tc (XLmlInt64 pass) + , tc (XLmlUInt64 pass) + , tc (XLmlChar pass) + , tc (XLmlString pass) + , tc (XXLit pass) + ) + +deriving instance (ForallLmlLit Show pass) => Show (LmlLit pass) +deriving instance (ForallLmlLit Eq pass) => Eq (LmlLit pass) diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Longident.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Longident.hs new file mode 100644 index 0000000..445e98a --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Longident.hs @@ -0,0 +1,15 @@ +-- | LamagraphML long identifiers +module Lamagraph.Compiler.Syntax.Longident (Longident (..), mkLongident, LLongident) where + +import Relude + +import Lamagraph.Compiler.Syntax.Extension + +-- | This type represents 'Text' dot-separated fragments in the source code. +newtype Longident = Longident (NonEmpty Text) + deriving (Show, Eq) + +mkLongident :: NonEmpty Text -> Longident +mkLongident = Longident + +type LLongident pass = XLocated pass Longident diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Pat.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Pat.hs new file mode 100644 index 0000000..fcc8674 --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Pat.hs @@ -0,0 +1,53 @@ +{-# LANGUAGE UndecidableInstances #-} + +-- | LamagraphML patterns +module Lamagraph.Compiler.Syntax.Pat (LLmlPat, LmlPat (..), ForallLmlPat) where + +import Relude + +import Lamagraph.Compiler.Syntax.Extension +import Lamagraph.Compiler.Syntax.Lit +import Lamagraph.Compiler.Syntax.Longident +import Lamagraph.Compiler.Syntax.Type + +-- | Located 'LmlPat' +type LLmlPat pass = XLocated pass (LmlPat pass) + +-- | LamagraphML pattern +data LmlPat pass + = -- | Pattern wildcard @_@ + LmlPatAny (XLmlPatAny pass) + | -- | Represents pattern variable as @x@ + LmlPatVar (XLmlPatVar pass) (XLocated pass Text) + | -- | Constant pattern + LmlPatConstant (XLmlPatConstant pass) (LmlLit pass) + | -- | Tuple pattern, invariant \(n \geq 2\) + LmlPatTuple (XLmlPatTuple pass) (LLmlPat pass) (NonEmpty (LLmlPat pass)) + | -- | Constructor application pattern + -- + -- Constructors aren't curried, this means that they must be applied to a tuple. + LmlPatConstruct (XLmlPatConstruct pass) (LLongident pass) (Maybe (LLmlPat pass)) + | -- | Pattern alternation @pat | pat@ + LmlPatOr (XLmlPatOr pass) (LLmlPat pass) (LLmlPat pass) + | -- | Pattern constrained with type, e.g. @(pat : typexpr)@ + LmlPatConstraint (XLmlPatConstraint pass) (LLmlPat pass) (LLmlType pass) + | XLmlPat !(XXPat pass) + +type ForallLmlPat (tc :: Type -> Constraint) pass = + ( tc (XLmlPatAny pass) + , tc (XLmlPatVar pass) + , tc (XLocated pass Text) + , tc (XLmlPatConstant pass) + , tc (LmlLit pass) + , tc (XLmlPatTuple pass) + , tc (LLmlPat pass) + , tc (XLmlPatConstruct pass) + , tc (LLongident pass) + , tc (XLmlPatOr pass) + , tc (XLmlPatConstraint pass) + , tc (LLmlType pass) + , tc (XXPat pass) + ) + +deriving instance (ForallLmlPat Show pass) => Show (LmlPat pass) +deriving instance (ForallLmlPat Eq pass) => Eq (LmlPat pass) diff --git a/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Type.hs b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Type.hs new file mode 100644 index 0000000..1f768f7 --- /dev/null +++ b/lamagraph-compiler/src/Lamagraph/Compiler/Syntax/Type.hs @@ -0,0 +1,47 @@ +{-# LANGUAGE UndecidableInstances #-} + +-- | LamagraphML type related types +module Lamagraph.Compiler.Syntax.Type (LLmlType, LmlType (..), ForallLmlType) where + +import Relude + +import Lamagraph.Compiler.Syntax.Extension +import Lamagraph.Compiler.Syntax.Longident + +-- | Located 'LmlType'. +type LLmlType pass = XLocated pass (LmlType pass) + +-- | LamagraphML type representation. +data LmlType pass + = -- TODO: Maybe we'll need something like RdrName instead of (XLocated pass Text) + + -- | Type variables like @'a@. + LmlTyVar (XLmlTyVar pass) (XLocated pass Text) + | -- | Type arrow like @'a -> 'a@. + LmlTyArrow (XLmlTyArrow pass) (LLmlType pass) (LLmlType pass) + | -- | Tuple on type level. + -- + -- /Invariant/: \(n \geq 2\) + LmlTyTuple (XLmlTyTuple pass) (LLmlType pass) (NonEmpty (LLmlType pass)) + | -- | Type constructor application. + -- @"'LmlTyConstr' _ lindent types"@ represents + -- + -- - @/typeconstr/@ when @types = []@ + -- - @/typexpr/ /typeconstr/@ when @types = [type]@ + -- - @( /typexpr/ { , /typexpr/ } ) /typeconstr/@ when @types = [type1, ..., typen]@ + LmlTyConstr (XLmlTyConstr pass) (LLongident pass) [LLmlType pass] + | XLmlType !(XXType pass) + +type ForallLmlType (tc :: Type -> Constraint) pass = + ( tc (XLmlTyVar pass) + , tc (XLocated pass Text) + , tc (XLmlTyArrow pass) + , tc (LLmlType pass) + , tc (XLmlTyTuple pass) + , tc (XLmlTyConstr pass) + , tc (LLongident pass) + , tc (XXType pass) + ) + +deriving instance (ForallLmlType Show pass) => Show (LmlType pass) +deriving instance (ForallLmlType Eq pass) => Eq (LmlType pass) diff --git a/lamagraph-compiler/src/Lib.hs b/lamagraph-compiler/src/Lib.hs deleted file mode 100644 index c4a903f..0000000 --- a/lamagraph-compiler/src/Lib.hs +++ /dev/null @@ -1,6 +0,0 @@ -module Lib ( - someFunc, -) where - -someFunc :: IO () -someFunc = putStrLn "someFunc" diff --git a/lamagraph-compiler/test/Lamagraph/Compiler/Parser/GoldenCommon.hs b/lamagraph-compiler/test/Lamagraph/Compiler/Parser/GoldenCommon.hs new file mode 100644 index 0000000..5f9ffc3 --- /dev/null +++ b/lamagraph-compiler/test/Lamagraph/Compiler/Parser/GoldenCommon.hs @@ -0,0 +1,28 @@ +module Lamagraph.Compiler.Parser.GoldenCommon ( + renderPretty, + parserGoldenTestsDir, + lmlExt, + changeFileDir, +) where + +import Relude + +import Prettyprinter +import Prettyprinter.Render.Text +import System.FilePath + +renderPretty :: Doc ann -> LText +renderPretty = renderLazy . layoutPretty (defaultLayoutOptions{layoutPageWidth = AvailablePerLine 80 1.0}) + +parserGoldenTestsDir :: FilePath +parserGoldenTestsDir = "test" "parserGolden" "source" + +lmlExt :: FilePath +lmlExt = ".lml" + +changeFileDir :: FilePath -> FilePath -> FilePath +changeFileDir filePath relativePath = newDir fileName + where + dir = takeDirectory filePath + newDir = normalise (dir relativePath) + fileName = takeFileName filePath diff --git a/lamagraph-compiler/test/Lamagraph/Compiler/Parser/LexerTest.hs b/lamagraph-compiler/test/Lamagraph/Compiler/Parser/LexerTest.hs new file mode 100644 index 0000000..2055b0d --- /dev/null +++ b/lamagraph-compiler/test/Lamagraph/Compiler/Parser/LexerTest.hs @@ -0,0 +1,259 @@ +module Lamagraph.Compiler.Parser.LexerTest (lexerUnitTests) where + +import Relude + +import Test.Tasty +import Test.Tasty.HUnit + +import Lamagraph.Compiler.Parser.LexerTypes +import Lamagraph.Compiler.Parser.LexerUtils + +skipWhitespace :: TestTree +skipWhitespace = + testCase "Skip whitespace" $ do + getTokenTypesFromText " \t\r\n" @?= Right [TokEOF] + +oneLevelMultilineComment :: TestTree +oneLevelMultilineComment = + testCase "Skip unnested multiline comment" $ do + getTokenTypesFromText "(* This is one level comment *)" @?= Right [TokEOF] + +nestedMultilineComments :: TestTree +nestedMultilineComments = + testCase "Handle nested multiline comments" $ do + getTokenTypesFromText comment @?= Right [TokEOF] + where + comment = + "(* First level of comment \n\ + \ (* Second level of comment*) \n\ + \ *)" + +singleLineComment :: TestTree +singleLineComment = + testCase "Skip single-line comments" $ do + getTokenTypesFromText "-- This is single line comment" @?= Right [TokEOF] + +noMixSingleLineAndMultiline :: TestTree +noMixSingleLineAndMultiline = + testCase "No mix of single-line and multiline comments" $ do + getTokenTypesFromText comment @?= Left "lexical error at line 2, column 2: Closing comment before opening" + where + comment = + "-- (* \n\ + \ *)" + +capitalizedIdent :: TestTree +capitalizedIdent = + testCase "Lex capitalized ident" $ do + getTokenTypesFromText "MyModule_42'meow" @?= Right [TokIdent Capitalized "MyModule_42'meow", TokEOF] + +lowercaseIdent :: TestTree +lowercaseIdent = + testCase "Lex lowercase ident" $ do + getTokenTypesFromText "_myType_42'meow" @?= Right [TokIdent Lowercase "_myType_42'meow", TokEOF] + +integerLiteral :: TestTree +integerLiteral = + testCase "Lex Int literal" $ do + getTokenTypesFromText "42" @?= Right [TokInt 42, TokEOF] + +int32Literal :: TestTree +int32Literal = + testCase "Lex Int32 literal" $ do + getTokenTypesFromText "42l" @?= Right [TokInt32 42, TokEOF] + +uint32Literal :: TestTree +uint32Literal = + testCase "Lex UInt32 literal" $ do + getTokenTypesFromText "42ul" @?= Right [TokUInt32 42, TokEOF] + +int64Literal :: TestTree +int64Literal = + testCase "Lex Int64 literal" $ do + getTokenTypesFromText "42L" @?= Right [TokInt64 42, TokEOF] + +uint64Literal :: TestTree +uint64Literal = + testCase "Lex UInt64 literal" $ do + getTokenTypesFromText "42UL" @?= Right [TokUInt64 42, TokEOF] + +allLetterKeywords :: TestTree -- Yep, strange name +allLetterKeywords = + testCase "Lex all letter keywords" $ do + getTokenTypesFromText str @?= Right tokens + where + str = + "and asr else false fun if in land let \ + \lor lsl lsr lxor match mod module \ + \of open rec then true type when with" + tokens = + [ TokAnd + , TokAsr + , TokElse + , TokFalse + , TokFun + , TokIf + , TokIn + , TokLand + , TokLet + , TokLor + , TokLsl + , TokLsr + , TokLxor + , TokMatch + , TokMod + , TokModule + , TokOf + , TokOpen + , TokRec + , TokThen + , TokTrue + , TokType + , TokWhen + , TokWith + , TokEOF + ] + +allSymbolKeywords :: TestTree +allSymbolKeywords = + testCase "Lex all symbol keywords" $ do + getTokenTypesFromText str @?= Right tokens + where + str = "&& ' ( ) * + , - -> : :: ; = [ ] _ . | ||" + tokens = + [ TokBoolAnd + , TokApostrophe + , TokLeftPar + , TokRightPar + , TokStar + , TokPlus + , TokComma + , TokMinus + , TokArrow + , TokColon + , TokDoubleColon + , TokSemicolon + , TokEq + , TokLeftBracket + , TokRightBracket + , TokWildcard + , TokDot + , TokBar + , TokDoubleBar + , TokEOF + ] + +infixSymbols :: TestTree +infixSymbols = + testCase "Lex infix symbols" $ do + getTokenTypesFromText "=@ <= >= |= &&& $! @. ^| += -** *+* / % **" + @?= Right + [ TokInfixSymbol0 "=@" + , TokInfixSymbol0 "<=" + , TokInfixSymbol0 ">=" + , TokInfixSymbol0 "|=" + , TokInfixSymbol0 "&&&" + , TokInfixSymbol0 "$!" + , TokInfixSymbol1 "@." + , TokInfixSymbol1 "^|" + , TokInfixSymbol2 "+=" + , TokInfixSymbol2 "-**" + , TokInfixSymbol3 "*+*" + , TokInfixSymbol3 "/" + , TokInfixSymbol3 "%" + , TokInfixSymbol4 "**" + , TokEOF + ] + +prefixSymbols :: TestTree +prefixSymbols = + testCase "Lex prefix symbols" $ do + getTokenTypesFromText "! != !@@. ?++ ~^|" + @?= Right + [ TokPrefixSymbol "!" + , TokPrefixSymbol "!=" + , TokPrefixSymbol "!@@." + , TokPrefixSymbol "?++" + , TokPrefixSymbol "~^|" + , TokEOF + ] + +regularString :: TestTree +regularString = + testCase "Lex regular string" $ do + getTokenTypesFromText "\"Regular string\"" + @?= Right [TokString "Regular string", TokEOF] + +escapedCharsInString :: TestTree +escapedCharsInString = + testCase "Lex escaped characters in string" $ do + getTokenTypesFromText "\"\\n \\\" \\\' \\\\\"" + @?= Right [TokString "\n \" \' \\", TokEOF] + +mixedCharsInString :: TestTree +mixedCharsInString = + testCase "Lex mixed string" $ do + getTokenTypesFromText "\"_myType_42\\\'meow is the best \\\\ identifier!\"" + @?= Right [TokString "_myType_42\'meow is the best \\ identifier!", TokEOF] + +regularChar :: TestTree +regularChar = + testCase "Lex regular char" $ do + getTokenTypesFromText "\'~\'" @?= Right [TokChar '~', TokEOF] + +escapedChars :: TestTree +escapedChars = + testCase "Lex escaped chars" $ do + getTokenTypesFromText "'\\n' '\\'' '\\\"' '\\\\'" + @?= Right [TokChar '\n', TokChar '\'', TokChar '\"', TokChar '\\', TokEOF] + +errorClosingCommentBeforeOpening :: TestTree +errorClosingCommentBeforeOpening = + testCase "Error: closing comment before opening" $ do + getTokenTypesFromText "*)" @?= Left "lexical error at line 1, column 1: Closing comment before opening" + +errorUnfinishedEscapeChar :: TestTree +errorUnfinishedEscapeChar = + testCase "Error: unfinished escape char" $ do + getTokenTypesFromText "\"\\ a\"" @?= Left "lexical error at line 1, column 2: Unfinished escape character" + +errorEOFInComment :: TestTree +errorEOFInComment = + testCase "Error: EOF in comment" $ do + getTokenTypesFromText "(* comment" @?= Left "lexical error: EOF while reading comment" + +errorEOFInString :: TestTree +errorEOFInString = + testCase "Error: EOF in comment" $ do + getTokenTypesFromText "\"string" @?= Left "lexical error: EOF while reading string" + +lexerUnitTests :: TestTree +lexerUnitTests = + testGroup + "Unit tests" + [ skipWhitespace + , oneLevelMultilineComment + , nestedMultilineComments + , singleLineComment + , noMixSingleLineAndMultiline + , capitalizedIdent + , lowercaseIdent + , integerLiteral + , int32Literal + , uint32Literal + , int64Literal + , uint64Literal + , regularChar + , escapedChars + , regularString + , escapedCharsInString + , mixedCharsInString + , infixSymbols + , prefixSymbols + , allLetterKeywords + , allSymbolKeywords + , errorClosingCommentBeforeOpening + , errorUnfinishedEscapeChar + , errorEOFInComment + , errorEOFInString + ] diff --git a/lamagraph-compiler/test/Lamagraph/Compiler/Parser/ParserRoundtrip.hs b/lamagraph-compiler/test/Lamagraph/Compiler/Parser/ParserRoundtrip.hs new file mode 100644 index 0000000..84d3976 --- /dev/null +++ b/lamagraph-compiler/test/Lamagraph/Compiler/Parser/ParserRoundtrip.hs @@ -0,0 +1,264 @@ +{-# LANGUAGE RecordWildCards #-} + +module Lamagraph.Compiler.Parser.ParserRoundtrip (prop_ParserRoundtrip) where + +import Relude + +import Data.List.NonEmpty.Extra qualified as NE +import Data.Text qualified as T +import Hedgehog +import Hedgehog.Gen qualified as Gen +import Hedgehog.Range qualified as Range +import Prettyprinter +import Prettyprinter.Render.Text + +import Lamagraph.Compiler.Extension +import Lamagraph.Compiler.Parser +import Lamagraph.Compiler.Parser.SrcLoc +import Lamagraph.Compiler.PrettyLml () +import Lamagraph.Compiler.Syntax + +{- | '<>' lifted to 'Applicative' +Very useful in this module because of 'Text' concatenation under 'Gen' monad +-} +(.<>.) :: (Applicative f, Semigroup c) => f c -> f c -> f c +a .<>. b = liftA2 (<>) a b + +keywords :: [Text] +keywords = + [ "and" + , "asr" + , "else" + , "false" + , "fun" + , "if" + , "in" + , "land" + , "let" + , "lor" + , "lsl" + , "lsr" + , "lxor" + , "match" + , "mod" + , "module" + , "of" + , "open" + , "rec" + , "then" + , "true" + , "type" + , "when" + , "with" + ] + +isKeyword :: Text -> Bool +isKeyword word = word `elem` keywords + +notKeyword :: Text -> Bool +notKeyword = not . isKeyword + +-- | Mostly 'Gen.alphaNum', but with @_@ +identChar :: Gen Char +identChar = Gen.element "abcdefghiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" + +identRange :: Range Int +identRange = Range.linear 0 11 + +nonEmptyRange :: Range Int +nonEmptyRange = Range.linear 1 6 + +listRange :: Range Int +listRange = Range.linear 0 6 + +declRange :: Range Int +declRange = Range.linear 0 200 + +{-# INLINE mkGenLoc #-} +mkGenLoc :: a -> Located a +mkGenLoc = L generatedSrcSpan + +genCapitalizedIdent :: Gen Text +genCapitalizedIdent = (T.singleton <$> Gen.upper) .<>. Gen.text identRange identChar + +genLCapitalizedIdent :: Gen (Located Text) +genLCapitalizedIdent = mkGenLoc <$> genCapitalizedIdent + +genLowercaseIdent :: Gen Text +genLowercaseIdent = + Gen.choice + [ Gen.filter notKeyword identStartingLetter + , Gen.constant "_" .<>. Gen.text (Range.linear 1 1) Gen.alphaNum .<>. Gen.text identRange identChar + ] + where + identStartingLetter = (T.singleton <$> Gen.lower) .<>. Gen.text identRange identChar + +genLLowercaseIdent :: Gen (Located Text) +genLLowercaseIdent = mkGenLoc <$> genLowercaseIdent + +genLongident :: Gen Text -> Gen Longident +genLongident genLastIdent = do + modulePath <- Gen.nonEmpty nonEmptyRange genCapitalizedIdent + mkLongident . NE.snoc modulePath <$> genLastIdent + +genLLongident :: Gen Text -> Gen (LLongident LmlcPs) +genLLongident genLastIdent = mkGenLoc <$> genLongident genLastIdent + +genValueName :: Gen Text +genValueName = Gen.choice [genLowercaseIdent, genPrefixSymbol, genInfixSymbolFiltered] + where + opTailRange = Range.linear 0 5 + genOpChar = Gen.element "!$%&*+./:<=>?@^|~" + genPrefixSymbol = + Gen.choice + [ pure "!" .<>. Gen.text opTailRange genOpChar + , Gen.choice [pure "?", pure "~"] .<>. Gen.text (Range.linear 1 5) genOpChar + ] + genInfixSymbol = (T.singleton <$> Gen.element "=<>@^|&+-*/$%") .<>. Gen.text opTailRange genOpChar + genInfixSymbolFiltered = Gen.filter (\x -> not (T.isPrefixOf "|" x || T.isPrefixOf "->" x)) genInfixSymbol + +genLValueName :: Gen (Located Text) +genLValueName = mkGenLoc <$> genValueName + +genIdent :: Gen Text +genIdent = + Gen.choice + [ genLowercaseIdent + , genCapitalizedIdent + ] + +genLIdent :: Gen (Located Text) +genLIdent = mkGenLoc <$> genIdent + +genChar :: Gen Char +genChar = Gen.enum '\32' '\126' + +genLmlDecl :: Gen (LmlDecl LmlcPs) +genLmlDecl = do + Gen.choice + [ OpenD noExtField <$> genOpenDecl + , genValD + , TyD noExtField <$> Gen.nonEmpty nonEmptyRange genLTyDecl + ] + +genValD :: Gen (LmlDecl LmlcPs) +genValD = do + binds <- Gen.nonEmpty nonEmptyRange genLLmlBind + flag <- Gen.element [NonRecursive, Recursive] + pure $ ValD noExtField flag binds + +genLLmlDecl :: Gen (LLmlDecl LmlcPs) +genLLmlDecl = mkGenLoc <$> genLmlDecl + +genOpenDecl :: Gen (OpenDecl LmlcPs) +genOpenDecl = do + OpenDecl noExtField <$> genLLongident genCapitalizedIdent + +genTyVars :: Gen [LLmlType LmlcPs] +genTyVars = Gen.list listRange (mkGenLoc . LmlTyVar noExtField <$> genLIdent) + +genTyDecl :: Gen (TyDecl LmlcPs) +genTyDecl = + Gen.choice + [ AliasDecl noExtField <$> genLLowercaseIdent <*> genTyVars <*> genLLmlType + , DataDecl noExtField <$> genLLowercaseIdent <*> genTyVars <*> Gen.list listRange genLConDecl + ] + +genLTyDecl :: Gen (LTyDecl LmlcPs) +genLTyDecl = mkGenLoc <$> genTyDecl + +genConDecl :: Gen (ConDecl LmlcPs) +genConDecl = ConDecl noExtField <$> genLCapitalizedIdent <*> Gen.list listRange genLLmlType + +genLConDecl :: Gen (LConDecl LmlcPs) +genLConDecl = mkGenLoc <$> genConDecl + +genLmlExpr :: Gen (LmlExpr LmlcPs) +genLmlExpr = + Gen.recursive + Gen.choice + [ LmlExprIdent noExtField <$> genLongident genValueName + , LmlExprConstant noExtField <$> genLmlLit + ] + [ LmlExprLet noExtField + <$> Gen.element [NonRecursive, Recursive] + <*> Gen.nonEmpty nonEmptyRange genLLmlBind + <*> genLLmlExpr + , LmlExprFunction noExtField <$> genLLmlPat <*> genLLmlExpr + , let func = (mkGenLoc . LmlExprIdent noExtField <$> genLongident genValueName) + in LmlExprApply noExtField <$> func <*> Gen.nonEmpty nonEmptyRange genLLmlExpr + , LmlExprMatch noExtField <$> genLLmlExpr <*> Gen.nonEmpty nonEmptyRange genLLmlCase + , LmlExprTuple noExtField <$> genLLmlExpr <*> Gen.nonEmpty nonEmptyRange genLLmlExpr + , LmlExprConstruct noExtField <$> genLLongident genCapitalizedIdent <*> Gen.maybe genLLmlExpr + , LmlExprIfThenElse noExtField <$> genLLmlExpr <*> genLLmlExpr <*> genLLmlExpr + , LmlExprConstraint noExtField <$> genLLmlExpr <*> genLLmlType + ] + +genLLmlExpr :: Gen (LLmlExpr LmlcPs) +genLLmlExpr = mkGenLoc <$> genLmlExpr + +genLmlBind :: Gen (LmlBind LmlcPs) +genLmlBind = LmlBind noExtField <$> genLLmlPat <*> genLLmlExpr + +genLLmlBind :: Gen (LLmlBind LmlcPs) +genLLmlBind = mkGenLoc <$> genLmlBind + +genLmlCase :: Gen (LmlCase LmlcPs) +genLmlCase = LmlCase noExtField <$> genLLmlPat <*> Gen.maybe genLLmlExpr <*> genLLmlExpr + +genLLmlCase :: Gen (LLmlCase LmlcPs) +genLLmlCase = mkGenLoc <$> genLmlCase + +genLmlLit :: Gen (LmlLit LmlcPs) +genLmlLit = + Gen.choice + [ LmlInt noExtField <$> Gen.int (Range.linear minBound maxBound) + , LmlInt32 noExtField <$> Gen.int32 (Range.linear minBound maxBound) + , LmlUInt32 noExtField <$> Gen.word32 (Range.linear minBound maxBound) + , LmlInt64 noExtField <$> Gen.int64 (Range.linear minBound maxBound) + , LmlUInt64 noExtField <$> Gen.word64 (Range.linear minBound maxBound) + , LmlChar noExtField <$> genChar + , LmlString noExtField <$> Gen.text (Range.linear 0 25) genChar + ] + +genLmlPat :: Gen (LmlPat LmlcPs) +genLmlPat = + Gen.recursive + Gen.choice + [ pure $ LmlPatAny noExtField + , LmlPatVar noExtField <$> genLValueName + , LmlPatConstant noExtField <$> genLmlLit + ] + [ LmlPatTuple noExtField <$> genLLmlPat <*> Gen.nonEmpty nonEmptyRange genLLmlPat + , LmlPatConstruct noExtField <$> genLLongident genCapitalizedIdent <*> Gen.maybe genLLmlPat + , LmlPatOr noExtField <$> genLLmlPat <*> genLLmlPat + , LmlPatConstraint noExtField <$> genLLmlPat <*> genLLmlType + ] + +genLLmlPat :: Gen (LLmlPat LmlcPs) +genLLmlPat = mkGenLoc <$> genLmlPat + +genLmlType :: Gen (LmlType LmlcPs) +genLmlType = + Gen.recursive + Gen.choice + [LmlTyVar noExtField <$> genLIdent] + [ LmlTyArrow noExtField <$> genLLmlType <*> genLLmlType + , LmlTyTuple noExtField <$> genLLmlType <*> Gen.nonEmpty nonEmptyRange genLLmlType + , LmlTyConstr noExtField <$> genLLongident genLowercaseIdent <*> Gen.list listRange genLLmlType + ] + +genLLmlType :: Gen (LLmlType LmlcPs) +genLLmlType = mkGenLoc <$> genLmlType + +genModule :: Gen (LmlModule LmlcPs) +genModule = do + let _lmlModExt = noExtField + _lmlModName <- Gen.maybe $ genLLongident genCapitalizedIdent + _lmlModDecls <- Gen.list declRange genLLmlDecl + pure LmlModule{..} + +prop_ParserRoundtrip :: Property +prop_ParserRoundtrip = withTests 100 . property $ do + asts <- forAll genModule + tripping asts (renderStrict . layoutPretty defaultLayoutOptions . pretty) parseLamagraphML diff --git a/lamagraph-compiler/test/Lamagraph/Compiler/Parser/PrettyAstGolden.hs b/lamagraph-compiler/test/Lamagraph/Compiler/Parser/PrettyAstGolden.hs new file mode 100644 index 0000000..f08ae18 --- /dev/null +++ b/lamagraph-compiler/test/Lamagraph/Compiler/Parser/PrettyAstGolden.hs @@ -0,0 +1,38 @@ +module Lamagraph.Compiler.Parser.PrettyAstGolden (parserPrettyAstGolden) where + +import Relude + +import Prettyprinter +import System.FilePath +import Test.Tasty +import Test.Tasty.Golden + +import Lamagraph.Compiler.Parser +import Lamagraph.Compiler.Parser.GoldenCommon +import Lamagraph.Compiler.PrettyAst () + +newExt :: String +newExt = "ast" + +newDir :: FilePath +newDir = ".." "ast" + +parserPrettyAstGolden :: IO TestTree +parserPrettyAstGolden = do + lmlFiles <- findByExtension [lmlExt] parserGoldenTestsDir + return $ + testGroup + "Pretty AST Golden tests" + [ goldenVsString (takeBaseName lmlFile) resLmlFile (helper lmlFile) + | lmlFile <- lmlFiles + , let resLmlFile = addExtension (changeFileDir lmlFile newDir) newExt + ] + where + helper :: FilePath -> IO LByteString + helper lmlFile = do + fileBS <- readFileBS lmlFile + let fileT = decodeUtf8 fileBS + parseResult = parseLamagraphML fileT + pure $ case parseResult of + Left err -> encodeUtf8 err + Right tree -> encodeUtf8 $ (renderPretty . pretty) tree diff --git a/lamagraph-compiler/test/Lamagraph/Compiler/Parser/PrettyLmlGolden.hs b/lamagraph-compiler/test/Lamagraph/Compiler/Parser/PrettyLmlGolden.hs new file mode 100644 index 0000000..bab5e3e --- /dev/null +++ b/lamagraph-compiler/test/Lamagraph/Compiler/Parser/PrettyLmlGolden.hs @@ -0,0 +1,32 @@ +module Lamagraph.Compiler.Parser.PrettyLmlGolden (parserPrettyLmlGolden) where + +import Relude + +import Prettyprinter +import System.FilePath +import Test.Tasty +import Test.Tasty.Golden + +import Lamagraph.Compiler.Parser +import Lamagraph.Compiler.Parser.GoldenCommon +import Lamagraph.Compiler.PrettyLml () + +parserPrettyLmlGolden :: IO TestTree +parserPrettyLmlGolden = do + lmlFiles <- findByExtension [lmlExt] parserGoldenTestsDir + return $ + testGroup + "Pretty LML Golden tests" + [ goldenVsString (takeBaseName lmlFile) resLmlFile (helper lmlFile) + | lmlFile <- lmlFiles + , let resLmlFile = changeFileDir lmlFile "../ppr" + ] + where + helper :: FilePath -> IO LByteString + helper lmlFile = do + fileBS <- readFileBS lmlFile + let fileT = decodeUtf8 fileBS + parseResult = parseLamagraphML fileT + pure $ case parseResult of + Left err -> encodeUtf8 err + Right tree -> encodeUtf8 $ (renderPretty . pretty) tree diff --git a/lamagraph-compiler/test/Spec.hs b/lamagraph-compiler/test/Spec.hs index cd4753f..6375130 100644 --- a/lamagraph-compiler/test/Spec.hs +++ b/lamagraph-compiler/test/Spec.hs @@ -1,2 +1,25 @@ +import Relude + +import Test.Tasty +import Test.Tasty.Hedgehog + +import Lamagraph.Compiler.Parser.LexerTest +import Lamagraph.Compiler.Parser.ParserRoundtrip +import Lamagraph.Compiler.Parser.PrettyAstGolden +import Lamagraph.Compiler.Parser.PrettyLmlGolden + main :: IO () -main = putStrLn "Test suite not yet implemented" +main = do + parserTests' <- parserTests + let tests = testGroup "Lamagraph Compiler" [lexerTests, parserTests'] + defaultMain tests + +lexerTests :: TestTree +lexerTests = testGroup "Lexer" [lexerUnitTests] + +parserTests :: IO TestTree +parserTests = do + parserASTGolden <- parserPrettyAstGolden + parserLmlGolden <- parserPrettyLmlGolden + let roundtrip = testPropertyNamed "Parser roundtrip (AST -> LML -> AST)" "prop_ParserRoundtrip" prop_ParserRoundtrip + return $ testGroup "Parser" [parserASTGolden, parserLmlGolden, roundtrip] diff --git a/lamagraph-compiler/test/parserGolden/ast/Arif.lml.ast b/lamagraph-compiler/test/parserGolden/ast/Arif.lml.ast new file mode 100644 index 0000000..316f8a6 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/Arif.lml.ast @@ -0,0 +1,37 @@ +(Module + (Nothing) + [ (L + { :1:1-18 } + (ValD NonRec + [ (L + { :1:5-18 } + (Bind + (L + { :1:5-6 } + (PatVar + (L + { :1:5-6 } + "x"))) + (L + { :1:9-18 } + (ExprApply + (L + { :1:11-12 } + (ExprIdent + "+")) + [ (L + { :1:9-10 } + (ExprConstant 1)) + , (L + { :1:13-18 } + (ExprApply + (L + { :1:15-16 } + (ExprIdent + "*")) + [ (L + { :1:13-14 } + (ExprConstant 2)) + , (L + { :1:17-18 } + (ExprConstant 3)) ])) ])))) ])) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/Const.lml.ast b/lamagraph-compiler/test/parserGolden/ast/Const.lml.ast new file mode 100644 index 0000000..27d7019 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/Const.lml.ast @@ -0,0 +1,110 @@ +(Module + (Just + (L + { :1:8-13 } + ("Const"))) + [ (L + { :3:1-11 } + (ValD NonRec + [ (L + { :3:5-11 } + (Bind + (L + { :3:5-7 } + (PatVar + (L + { :3:5-7 } + "x1"))) + (L + { :3:10-11 } + (ExprConstant 1)))) ])) + , (L + { :4:1-12 } + (ValD NonRec + [ (L + { :4:5-12 } + (Bind + (L + { :4:5-7 } + (PatVar + (L + { :4:5-7 } + "x2"))) + (L + { :4:10-12 } + (ExprConstant 1l)))) ])) + , (L + { :5:1-13 } + (ValD NonRec + [ (L + { :5:5-13 } + (Bind + (L + { :5:5-7 } + (PatVar + (L + { :5:5-7 } + "x3"))) + (L + { :5:10-13 } + (ExprConstant 1ul)))) ])) + , (L + { :6:1-12 } + (ValD NonRec + [ (L + { :6:5-12 } + (Bind + (L + { :6:5-7 } + (PatVar + (L + { :6:5-7 } + "x4"))) + (L + { :6:10-12 } + (ExprConstant 1L)))) ])) + , (L + { :7:1-13 } + (ValD NonRec + [ (L + { :7:5-13 } + (Bind + (L + { :7:5-7 } + (PatVar + (L + { :7:5-7 } + "x5"))) + (L + { :7:10-13 } + (ExprConstant 1UL)))) ])) + , (L + { :8:1-13 } + (ValD NonRec + [ (L + { :8:5-13 } + (Bind + (L + { :8:5-7 } + (PatVar + (L + { :8:5-7 } + "x6"))) + (L + { :8:10-13 } + (ExprConstant 'a')))) ])) + , (L + { :9:1-14 } + (ValD NonRec + [ (L + { :9:5-14 } + (Bind + (L + { :9:5-7 } + (PatVar + (L + { :9:5-7 } + "x7"))) + (L + { :9:10-14 } + (ExprConstant "ab")))) ])) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/ConstrPatterns.lml.ast b/lamagraph-compiler/test/parserGolden/ast/ConstrPatterns.lml.ast new file mode 100644 index 0000000..571c2e1 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/ConstrPatterns.lml.ast @@ -0,0 +1,191 @@ +(Module + (Just + (L + { :1:8-22 } + ("ConstrPatterns"))) + [ (L + { :3:1-36 } + (ValD NonRec + [ (L + { :3:5-36 } + (Bind + (L + { :3:5-19 } + (PatConstruct + (L + { :3:6-16 } + ("SomeConstr")) + (Just + (L + { :3:17-18 } + (PatAny))))) + (L + { :3:22-36 } + (ExprConstruct + (L + { :3:23-33 } + ("SomeConstr")) + (Just + (L + { :3:34-35 } + (ExprConstant 1))))))) ])) + , (L + { :4:1-16 } + (ValD NonRec + [ (L + { :4:5-16 } + (Bind + (L + { :4:5-9 } + (PatConstruct + (L + { :4:5-9 } + ("true")) + (Nothing))) + (L + { :4:12-16 } + (ExprConstruct + (L + { :4:12-16 } + ("true")) + (Nothing))))) ])) + , (L + { :5:1-18 } + (ValD NonRec + [ (L + { :5:5-18 } + (Bind + (L + { :5:5-10 } + (PatConstruct + (L + { :5:5-10 } + ("false")) + (Nothing))) + (L + { :5:13-18 } + (ExprConstruct + (L + { :5:13-18 } + ("false")) + (Nothing))))) ])) + , (L + { :6:1-12 } + (ValD NonRec + [ (L + { :6:5-12 } + (Bind + (L + { :6:5-7 } + (PatConstruct + (L + { :6:5-7 } + ("()")) + (Nothing))) + (L + { :6:10-12 } + (ExprConstruct + (L + { :6:10-12 } + ("()")) + (Nothing))))) ])) + , (L + { :7:1-12 } + (ValD NonRec + [ (L + { :7:5-12 } + (Bind + (L + { :7:5-7 } + (PatConstruct + (L + { :7:5-7 } + ("[]")) + (Nothing))) + (L + { :7:10-12 } + (ExprConstruct + (L + { :7:10-12 } + ("[]")) + (Nothing))))) ])) + , (L + { :8:1-22 } + (ValD NonRec + [ (L + { :8:5-22 } + (Bind + (L + { :8:5-13 } + (PatConstruct + (L + { :8:6-8 } + ("::")) + (Just + (L + { :8:5-13 } + (PatTuple + [ (L + { :8:5-6 } + (PatVar + (L + { :8:5-6 } + "a"))) + , (L + { :8:8-13 } + (PatConstruct + (L + { :8:9-11 } + ("::")) + (Just + (L + { :8:8-13 } + (PatTuple + [ (L + { :8:8-9 } + (PatVar + (L + { :8:8-9 } + "b"))) + , (L + { :8:11-13 } + (PatConstruct + (L + { :8:11-13 } + ("[]")) + (Nothing))) ]))))) ]))))) + (L + { :8:17-22 } + (ExprConstruct + (L + { } + ("::")) + (Just + (L + { :8:17-22 } + (ExprTuple + [ (L + { :8:17-18 } + (ExprIdent + "a")) + , (L + { :8:20-22 } + (ExprConstruct + (L + { } + ("::")) + (Just + (L + { :8:20-22 } + (ExprTuple + [ (L + { :8:20-21 } + (ExprIdent + "b")) + , (L + { :8:21-22 } + (ExprConstruct + (L + { } + ("[]")) + (Nothing))) ]))))) ]))))))) ])) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/EmptyModule.lml.ast b/lamagraph-compiler/test/parserGolden/ast/EmptyModule.lml.ast new file mode 100644 index 0000000..1ada117 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/EmptyModule.lml.ast @@ -0,0 +1,6 @@ +(Module + (Just + (L + { :1:8-19 } + ("EmptyModule"))) + []) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/Fac.lml.ast b/lamagraph-compiler/test/parserGolden/ast/Fac.lml.ast new file mode 100644 index 0000000..2091dad --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/Fac.lml.ast @@ -0,0 +1,134 @@ +(Module + (Just + (L + { :1:8-11 } + ("Fac"))) + [ (L + { :3:1-12 } + (OpenD + OpenDecl + (L + { :3:6-12 } + ("Stdlib")))) + , (L + { :(5,1)-(10,13) } + (ValD NonRec + [ (L + { :(5,5)-(10,13) } + (Bind + (L + { :5:5-8 } + (PatVar + (L + { :5:5-8 } + "fac"))) + (L + { :(5,9)-(10,13) } + (ExprFunction + (L + { :5:9-10 } + (PatVar + (L + { :5:9-10 } + "n"))) + (L + { :(6,3)-(10,13) } + (ExprLet Rec + [ (L + { :(6,11)-(8,34) } + (Bind + (L + { :6:11-17 } + (PatVar + (L + { :6:11-17 } + "helper"))) + (L + { :(6,18)-(8,34) } + (ExprFunction + (L + { :6:18-19 } + (PatVar + (L + { :6:18-19 } + "m"))) + (L + { :(6,20)-(8,34) } + (ExprFunction + (L + { :6:20-23 } + (PatVar + (L + { :6:20-23 } + "acc"))) + (L + { :(7,5)-(8,34) } + (ExprITE + (L + { :7:8-13 } + (ExprApply + (L + { :7:10-11 } + (ExprIdent + ">")) + [ (L + { :7:8-9 } + (ExprIdent + "m")) + , (L + { :7:12-13 } + (ExprIdent + "n")) ])) + (L + { :7:19-22 } + (ExprIdent + "acc")) + (L + { :8:10-34 } + (ExprApply + (L + { :8:10-16 } + (ExprIdent + "helper")) + [ (L + { :8:17-24 } + (ExprApply + (L + { :8:20-21 } + (ExprIdent + "+")) + [ (L + { :8:18-19 } + (ExprIdent + "m")) + , (L + { :8:22-23 } + (ExprConstant 1)) ])) + , (L + { :8:25-34 } + (ExprApply + (L + { :8:30-31 } + (ExprIdent + "*")) + [ (L + { :8:26-29 } + (ExprIdent + "acc")) + , (L + { :8:32-33 } + (ExprIdent + "m")) ])) ])))))))))) ] + (L + { :10:3-13 } + (ExprApply + (L + { :10:3-9 } + (ExprIdent + "helper")) + [ (L + { :10:10-11 } + (ExprConstant 1)) + , (L + { :10:12-13 } + (ExprConstant 1)) ])))))))) ])) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/FuncApp.lml.ast b/lamagraph-compiler/test/parserGolden/ast/FuncApp.lml.ast new file mode 100644 index 0000000..02484a6 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/FuncApp.lml.ast @@ -0,0 +1,29 @@ +(Module + (Nothing) + [ (L + { :1:1-14 } + (ValD NonRec + [ (L + { :1:5-14 } + (Bind + (L + { :1:5-6 } + (PatVar + (L + { :1:5-6 } + "x"))) + (L + { :1:9-14 } + (ExprApply + (L + { :1:9-10 } + (ExprIdent + "f")) + [ (L + { :1:11-12 } + (ExprIdent + "y")) + , (L + { :1:13-14 } + (ExprIdent + "z")) ])))) ])) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/KakaduExample.lml.ast b/lamagraph-compiler/test/parserGolden/ast/KakaduExample.lml.ast new file mode 100644 index 0000000..3f0acf8 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/KakaduExample.lml.ast @@ -0,0 +1,56 @@ +(Module + (Just + (L + { :1:8-21 } + ("KakaduExample"))) + [ (L + { :3:1-31 } + (ValD NonRec + [ (L + { :3:5-31 } + (Bind + (L + { :3:5-6 } + (PatVar + (L + { :3:5-6 } + "x"))) + (L + { :3:9-31 } + (ExprApply + (L + { :3:11-12 } + (ExprIdent + "+")) + [ (L + { :3:9-10 } + (ExprConstant 1)) + , (L + { :3:13-31 } + (ExprLet NonRec + [ (L + { :3:17-22 } + (Bind + (L + { :3:17-18 } + (PatVar + (L + { :3:17-18 } + "y"))) + (L + { :3:21-22 } + (ExprConstant 5)))) ] + (L + { :3:26-31 } + (ExprApply + (L + { :3:28-29 } + (ExprIdent + "+")) + [ (L + { :3:26-27 } + (ExprConstant 5)) + , (L + { :3:30-31 } + (ExprIdent + "y")) ])))) ])))) ])) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/ListPatterns.lml.ast b/lamagraph-compiler/test/parserGolden/ast/ListPatterns.lml.ast new file mode 100644 index 0000000..fc0939f --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/ListPatterns.lml.ast @@ -0,0 +1,169 @@ +(Module + (Just + (L + { :1:8-20 } + ("ListPatterns"))) + [ (L + { :3:1-11 } + (ValD NonRec + [ (L + { :3:5-11 } + (Bind + (L + { :3:5-7 } + (PatConstruct + (L + { :3:5-7 } + ("[]")) + (Nothing))) + (L + { :3:10-11 } + (ExprIdent + "x")))) ])) + , (L + { :4:1-12 } + (ValD NonRec + [ (L + { :4:6-12 } + (Bind + (L + { :4:6-8 } + (PatConstruct + (L + { } + ("::")) + (Just + (L + { :4:6-8 } + (PatTuple + [ (L + { :4:6-7 } + (PatVar + (L + { :4:6-7 } + "a"))) + , (L + { :4:7-8 } + (PatConstruct + (L + { } + ("[]")) + (Nothing))) ]))))) + (L + { :4:11-12 } + (ExprIdent + "x")))) ])) + , (L + { :5:1-15 } + (ValD NonRec + [ (L + { :5:6-15 } + (Bind + (L + { :5:6-11 } + (PatConstruct + (L + { } + ("::")) + (Just + (L + { :5:6-11 } + (PatTuple + [ (L + { :5:6-7 } + (PatVar + (L + { :5:6-7 } + "a"))) + , (L + { :5:9-11 } + (PatConstruct + (L + { } + ("::")) + (Just + (L + { :5:9-11 } + (PatTuple + [ (L + { :5:9-10 } + (PatVar + (L + { :5:9-10 } + "b"))) + , (L + { :5:10-11 } + (PatConstruct + (L + { } + ("[]")) + (Nothing))) ]))))) ]))))) + (L + { :5:14-15 } + (ExprIdent + "x")))) ])) + , (L + { :6:1-18 } + (ValD NonRec + [ (L + { :6:6-18 } + (Bind + (L + { :6:6-14 } + (PatConstruct + (L + { } + ("::")) + (Just + (L + { :6:6-14 } + (PatTuple + [ (L + { :6:6-7 } + (PatVar + (L + { :6:6-7 } + "a"))) + , (L + { :6:9-14 } + (PatConstruct + (L + { } + ("::")) + (Just + (L + { :6:9-14 } + (PatTuple + [ (L + { :6:9-10 } + (PatVar + (L + { :6:9-10 } + "b"))) + , (L + { :6:12-14 } + (PatConstruct + (L + { } + ("::")) + (Just + (L + { :6:12-14 } + (PatTuple + [ (L + { :6:12-13 } + (PatVar + (L + { :6:12-13 } + "c"))) + , (L + { :6:13-14 } + (PatConstruct + (L + { } + ("[]")) + (Nothing))) ]))))) ]))))) ]))))) + (L + { :6:17-18 } + (ExprIdent + "x")))) ])) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/MatchPatAssoc.lml.ast b/lamagraph-compiler/test/parserGolden/ast/MatchPatAssoc.lml.ast new file mode 100644 index 0000000..44f3c05 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/MatchPatAssoc.lml.ast @@ -0,0 +1,95 @@ +(Module + (Nothing) + [ (L + { :(1,1)-(4,23) } + (ValD NonRec + [ (L + { :(1,5)-(4,23) } + (Bind + (L + { :1:5-6 } + (PatVar + (L + { :1:5-6 } + "f"))) + (L + { :(1,9)-(4,23) } + (ExprMatch + (L + { :1:15-16 } + (ExprIdent + "x")) + [ (L + { :2:13-30 } + (Case + (L + { :2:13-22 } + (ParOr + (L + { :2:13-18 } + (ParOr + (L + { :2:13-14 } + (PatConstant + 1)) + (L + { :2:17-18 } + (PatConstant + 2)))) + (L + { :2:21-22 } + (PatConstant + 3)))) + (Nothing) + (L + { :2:26-30 } + (ExprConstruct + (L + { :2:26-30 } + ("true")) + (Nothing))))) + , (L + { :3:13-34 } + (Case + (L + { :3:13-14 } + (PatVar + (L + { :3:13-14 } + "x"))) + (Just + (L + { :3:20-26 } + (ExprApply + (L + { :3:22-24 } + (ExprIdent + "==")) + [ (L + { :3:20-21 } + (ExprIdent + "x")) + , (L + { :3:25-26 } + (ExprConstant 4)) ]))) + (L + { :3:30-34 } + (ExprConstruct + (L + { :3:30-34 } + ("true")) + (Nothing))))) + , (L + { :4:13-23 } + (Case + (L + { :4:13-14 } + (PatAny)) + (Nothing) + (L + { :4:18-23 } + (ExprConstruct + (L + { :4:18-23 } + ("false")) + (Nothing))))) ])))) ])) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/Opens.lml.ast b/lamagraph-compiler/test/parserGolden/ast/Opens.lml.ast new file mode 100644 index 0000000..9e7905c --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/Opens.lml.ast @@ -0,0 +1,19 @@ +(Module + (Just + (L + { :1:8-13 } + ("Opens"))) + [ (L + { :3:1-8 } + (OpenD + OpenDecl + (L + { :3:6-8 } + ("M1")))) + , (L + { :5:1-20 } + (OpenD + OpenDecl + (L + { :5:6-20 } + ("Stdlib, Fun, Abs")))) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/Types.lml.ast b/lamagraph-compiler/test/parserGolden/ast/Types.lml.ast new file mode 100644 index 0000000..2a4cb5a --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/Types.lml.ast @@ -0,0 +1,395 @@ +(Module + (Just + (L + { :1:8-13 } + ("Types"))) + [ (L + { :3:1-10 } + (TyD + [(L { :3:6-10 } (DataDecl (L { :3:6-10 } "unit") [] []))])) + , (L + { :5:1-37 } + (TyD + [ (L + { :5:6-37 } + (AliasDecl + (L + { :5:9-14 } + "alias") + [(L { :5:6-8 } (TyVar (L { :5:7-8 } "a")))] + (L + { :5:17-37 } + (TyArrow + (L + { :5:17-20 } + (TyConstr + (L + { :5:17-20 } + ("int")) + [])) + (L + { :5:24-37 } + (TyArrow + (L + { :5:24-31 } + (TyConstr + (L + { :5:27-31 } + ("list")) + [(L { :5:24-26 } (TyVar (L { :5:25-26 } "a")))])) + (L + { :5:35-37 } + (TyVar + (L + { :5:36-37 } + "a"))))))))) ])) + , (L + { :7:1-27 } + (TyD + [ (L + { :7:6-27 } + (DataDecl + (L + { :7:6-10 } + "data") + [] + [ (L + { :7:13-22 } + (ConDecl + (L + { :7:13-15 } + "C1") + [(L { :7:19-22 } (TyConstr (L { :7:19-22 } ("int")) []))])) + , (L + { :7:25-27 } + (ConDecl + (L + { :7:25-27 } + "C2") + [])) ])) ])) + , (L + { :9:1-47 } + (TyD + [ (L + { :9:6-47 } + (DataDecl + (L + { :9:9-13 } + "list") + [(L { :9:6-8 } (TyVar (L { :9:7-8 } "a")))] + [ (L + { :9:16-24 } + (ConDecl + (L + { :9:16-18 } + "[]") + [(L { :9:22-24 } (TyVar (L { :9:23-24 } "a")))])) + , (L + { :9:27-47 } + (ConDecl + (L + { :9:27-31 } + "::") + [ (L + { :9:35-37 } + (TyVar + (L + { :9:36-37 } + "a"))) + , (L + { :9:40-47 } + (TyConstr + (L + { :9:43-47 } + ("list")) + [(L { :9:40-42 } (TyVar (L { :9:41-42 } "a")))])) ])) ])) ])) + , (L + { :11:1-48 } + (TyD + [ (L + { :11:7-48 } + (DataDecl + (L + { :11:15-21 } + "either") + [ (L + { :11:7-9 } + (TyVar + (L + { :11:8-9 } + "a"))) + , (L + { :11:11-13 } + (TyVar + (L + { :11:12-13 } + "b"))) ] + [ (L + { :11:24-34 } + (ConDecl + (L + { :11:24-28 } + "Left") + [(L { :11:32-34 } (TyVar (L { :11:33-34 } "a")))])) + , (L + { :11:37-48 } + (ConDecl + (L + { :11:37-42 } + "Right") + [(L { :11:46-48 } (TyVar (L { :11:47-48 } "b")))])) ])) ])) + , (L + { :13:1-34 } + (TyD + [ (L + { :13:6-34 } + (DataDecl + (L + { :13:6-15 } + "someTuple") + [] + [ (L + { :13:18-34 } + (ConDecl + (L + { :13:18-19 } + "T") + [ (L + { :13:23-34 } + (TyTuple + [ (L + { :13:24-27 } + (TyConstr + (L + { :13:24-27 } + ("int")) + [])) + , (L + { :13:30-33 } + (TyConstr + (L + { :13:30-33 } + ("int")) + [])) ])) ])) ])) ])) + , (L + { :15:1-108 } + (TyD + [ (L + { :15:7-108 } + (AliasDecl + (L + { :15:19-34 } + "weirdTupleAlias") + [ (L + { :15:7-9 } + (TyVar + (L + { :15:8-9 } + "a"))) + , (L + { :15:11-13 } + (TyVar + (L + { :15:12-13 } + "b"))) + , (L + { :15:15-17 } + (TyVar + (L + { :15:16-17 } + "c"))) ] + (L + { :15:37-108 } + (TyArrow + (L + { :15:37-77 } + (TyArrow + (L + { :15:38-52 } + (TyConstr + (L + { :15:48-52 } + ("list")) + [ (L + { :15:38-47 } + (TyTuple + [ (L + { :15:39-41 } + (TyVar + (L + { :15:40-41 } + "a"))) + , (L + { :15:44-46 } + (TyVar + (L + { :15:45-46 } + "b"))) ])) ])) + (L + { :15:56-76 } + (TyConstr + (L + { :15:72-76 } + ("list")) + [ (L + { :15:56-71 } + (TyConstr + (L + { :15:65-71 } + ("either")) + [ (L + { :15:57-59 } + (TyVar + (L + { :15:58-59 } + "c"))) + , (L + { :15:61-63 } + (TyVar + (L + { :15:62-63 } + "b"))) ])) ])))) + (L + { :15:81-108 } + (TyArrow + (L + { :15:81-102 } + (TyConstr + (L + { :15:96-102 } + ("either")) + [ (L + { :15:82-90 } + (TyArrow + (L + { :15:82-84 } + (TyVar + (L + { :15:83-84 } + "a"))) + (L + { :15:88-90 } + (TyVar + (L + { :15:89-90 } + "b"))))) + , (L + { :15:92-94 } + (TyVar + (L + { :15:93-94 } + "c"))) ])) + (L + { :15:106-108 } + (TyVar + (L + { :15:107-108 } + "c"))))))))) ])) + , (L + { :(17,1)-(19,33) } + (TyD + [ (L + { :(17,6)-(19,33) } + (DataDecl + (L + { :17:6-20 } + "tooMuchConstrs") + [] + [ (L + { :17:23-60 } + (ConDecl + (L + { :17:23-25 } + "C1") + [ (L + { :17:29-60 } + (TyArrow + (L + { :17:30-33 } + (TyConstr + (L + { :17:30-33 } + ("int")) + [])) + (L + { :17:37-59 } + (TyArrow + (L + { :17:37-45 } + (TyConstr + (L + { :17:41-45 } + ("list")) + [ (L + { :17:37-40 } + (TyConstr + (L + { :17:37-40 } + ("int")) + [])) ])) + (L + { :17:49-59 } + (TyConstr + (L + { :17:53-59 } + ("either")) + [ (L + { :17:49-52 } + (TyConstr + (L + { :17:49-52 } + ("int")) + [])) ])))))) ])) + , (L + { :18:23-54 } + (ConDecl + (L + { :18:23-25 } + "C2") + [ (L + { :18:29-54 } + (TyArrow + (L + { :18:30-44 } + (TyConstr + (L + { :18:30-44 } + ("tooMuchConstrs")) + [])) + (L + { :18:48-53 } + (TyConstr + (L + { :18:48-53 } + ("int32")) + [])))) ])) + , (L + { :19:23-33 } + (ConDecl + (L + { :19:23-25 } + "C3") + [ (L + { :19:29-33 } + (TyConstr + (L + { :19:29-33 } + ("bool")) + [])) ])) ])) ])) + , (L + { :21:1-17 } + (TyD + [ (L + { :21:6-17 } + (AliasDecl + (L + { :21:9-11 } + "_t") + [(L { :21:6-8 } (TyVar (L { :21:7-8 } "a")))] + (L + { :21:14-17 } + (TyConstr + (L + { :21:14-17 } + ("int")) + [])))) ])) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ast/ValuePath.lml.ast b/lamagraph-compiler/test/parserGolden/ast/ValuePath.lml.ast new file mode 100644 index 0000000..d995be6 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ast/ValuePath.lml.ast @@ -0,0 +1,31 @@ +(Module + (Nothing) + [ (L + { :(1,1)-(2,12) } + (ValD NonRec + [ (L + { :1:5-12 } + (Bind + (L + { :1:5-6 } + (PatVar + (L + { :1:5-6 } + "x"))) + (L + { :1:9-12 } + (ExprIdent + "M, y")))) + , (L + { :2:5-12 } + (Bind + (L + { :2:5-6 } + (PatVar + (L + { :2:5-6 } + "y"))) + (L + { :2:9-12 } + (ExprIdent + "M, x")))) ])) ]) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/Arif.lml b/lamagraph-compiler/test/parserGolden/ppr/Arif.lml new file mode 100644 index 0000000..4253d4d --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/Arif.lml @@ -0,0 +1 @@ +let x = ( + ) (1) (( * ) (2) (3)) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/Const.lml b/lamagraph-compiler/test/parserGolden/ppr/Const.lml new file mode 100644 index 0000000..2477558 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/Const.lml @@ -0,0 +1,15 @@ +module Const + +let x1 = 1 + +let x2 = 1l + +let x3 = 1ul + +let x4 = 1L + +let x5 = 1UL + +let x6 = 'a' + +let x7 = "ab" \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/ConstrPatterns.lml b/lamagraph-compiler/test/parserGolden/ppr/ConstrPatterns.lml new file mode 100644 index 0000000..ecc7002 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/ConstrPatterns.lml @@ -0,0 +1,13 @@ +module ConstrPatterns + +let (SomeConstr (_)) = (SomeConstr (1)) + +let true = true + +let false = false + +let () = () + +let [] = [] + +let (a :: (b :: [])) = (a :: (b :: [])) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/EmptyModule.lml b/lamagraph-compiler/test/parserGolden/ppr/EmptyModule.lml new file mode 100644 index 0000000..d79e8de --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/EmptyModule.lml @@ -0,0 +1 @@ +module EmptyModule \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/Fac.lml b/lamagraph-compiler/test/parserGolden/ppr/Fac.lml new file mode 100644 index 0000000..1465787 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/Fac.lml @@ -0,0 +1,6 @@ +module Fac + +open Stdlib + +let fac = fun n -> let rec helper = +fun m -> fun acc -> if ( > ) (m) (n) then acc else helper (( + ) (m) (1)) (( * ) (acc) (m)) in helper (1) (1) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/FuncApp.lml b/lamagraph-compiler/test/parserGolden/ppr/FuncApp.lml new file mode 100644 index 0000000..b2ce741 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/FuncApp.lml @@ -0,0 +1 @@ +let x = f (y) (z) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/KakaduExample.lml b/lamagraph-compiler/test/parserGolden/ppr/KakaduExample.lml new file mode 100644 index 0000000..9bcf094 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/KakaduExample.lml @@ -0,0 +1,3 @@ +module KakaduExample + +let x = ( + ) (1) (let y = 5 in ( + ) (5) (y)) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/ListPatterns.lml b/lamagraph-compiler/test/parserGolden/ppr/ListPatterns.lml new file mode 100644 index 0000000..899f856 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/ListPatterns.lml @@ -0,0 +1,9 @@ +module ListPatterns + +let [] = x + +let (a :: []) = x + +let (a :: (b :: [])) = x + +let (a :: (b :: (c :: []))) = x \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/MatchPatAssoc.lml b/lamagraph-compiler/test/parserGolden/ppr/MatchPatAssoc.lml new file mode 100644 index 0000000..7143e6c --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/MatchPatAssoc.lml @@ -0,0 +1,3 @@ +let f = (match x with ((1 | 2) | 3) -> true + | x when ( == ) (x) (4) -> true + | _ -> false) \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/Opens.lml b/lamagraph-compiler/test/parserGolden/ppr/Opens.lml new file mode 100644 index 0000000..66de068 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/Opens.lml @@ -0,0 +1,5 @@ +module Opens + +open M1 + +open Stdlib.Fun.Abs \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/Types.lml b/lamagraph-compiler/test/parserGolden/ppr/Types.lml new file mode 100644 index 0000000..56176af --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/Types.lml @@ -0,0 +1,22 @@ +module Types + +type unit + +type 'a alias = (int -> ('a list -> 'a)) + +type data = C1 of int | C2 + +type 'a list = [] of 'a | (::) of 'a * 'a list + +type ('a, 'b) either = Left of 'a | Right of 'b + +type someTuple = T of (int * int) + +type ('a, 'b, 'c) weirdTupleAlias = ((('a * 'b) list -> ('c, 'b) either list) -> +((('a -> 'b), 'c) either -> 'c)) + +type tooMuchConstrs = C1 of (int -> (int list -> int either)) + | C2 of (tooMuchConstrs -> int32) + | C3 of bool + +type 'a _t = int \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/ppr/ValuePath.lml b/lamagraph-compiler/test/parserGolden/ppr/ValuePath.lml new file mode 100644 index 0000000..402d17e --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/ppr/ValuePath.lml @@ -0,0 +1,3 @@ +let x = M.y +and +y = M.x \ No newline at end of file diff --git a/lamagraph-compiler/test/parserGolden/source/Arif.lml b/lamagraph-compiler/test/parserGolden/source/Arif.lml new file mode 100644 index 0000000..9de1f64 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/Arif.lml @@ -0,0 +1 @@ +let x = 1 + 2 * 3 diff --git a/lamagraph-compiler/test/parserGolden/source/Const.lml b/lamagraph-compiler/test/parserGolden/source/Const.lml new file mode 100644 index 0000000..bebc129 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/Const.lml @@ -0,0 +1,9 @@ +module Const + +let x1 = 1 +let x2 = 1l +let x3 = 1ul +let x4 = 1L +let x5 = 1UL +let x6 = 'a' +let x7 = "ab" diff --git a/lamagraph-compiler/test/parserGolden/source/ConstrPatterns.lml b/lamagraph-compiler/test/parserGolden/source/ConstrPatterns.lml new file mode 100644 index 0000000..a46d69c --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/ConstrPatterns.lml @@ -0,0 +1,8 @@ +module ConstrPatterns + +let (SomeConstr _) = (SomeConstr 1) +let true = true +let false = false +let () = () +let [] = [] +let a::b::[] = [a; b] diff --git a/lamagraph-compiler/test/parserGolden/source/EmptyModule.lml b/lamagraph-compiler/test/parserGolden/source/EmptyModule.lml new file mode 100644 index 0000000..a0245e7 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/EmptyModule.lml @@ -0,0 +1 @@ +module EmptyModule diff --git a/lamagraph-compiler/test/parserGolden/source/Fac.lml b/lamagraph-compiler/test/parserGolden/source/Fac.lml new file mode 100644 index 0000000..cd9f154 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/Fac.lml @@ -0,0 +1,10 @@ +module Fac + +open Stdlib + +let fac n = + let rec helper m acc = + if m > n then acc + else helper (m + 1) (acc * m) + in + helper 1 1 diff --git a/lamagraph-compiler/test/parserGolden/source/FuncApp.lml b/lamagraph-compiler/test/parserGolden/source/FuncApp.lml new file mode 100644 index 0000000..8b059b0 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/FuncApp.lml @@ -0,0 +1 @@ +let x = f y z diff --git a/lamagraph-compiler/test/parserGolden/source/KakaduExample.lml b/lamagraph-compiler/test/parserGolden/source/KakaduExample.lml new file mode 100644 index 0000000..85e7ebe --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/KakaduExample.lml @@ -0,0 +1,3 @@ +module KakaduExample + +let x = 1 + let y = 5 in 5 + y diff --git a/lamagraph-compiler/test/parserGolden/source/ListPatterns.lml b/lamagraph-compiler/test/parserGolden/source/ListPatterns.lml new file mode 100644 index 0000000..4c50b94 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/ListPatterns.lml @@ -0,0 +1,6 @@ +module ListPatterns + +let [] = x +let [a] = x +let [a; b] = x +let [a; b; c] = x diff --git a/lamagraph-compiler/test/parserGolden/source/MatchPatAssoc.lml b/lamagraph-compiler/test/parserGolden/source/MatchPatAssoc.lml new file mode 100644 index 0000000..ebe45fc --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/MatchPatAssoc.lml @@ -0,0 +1,4 @@ +let f = match x with + | 1 | 2 | 3 -> true + | x when x == 4 -> true + | _ -> false diff --git a/lamagraph-compiler/test/parserGolden/source/Opens.lml b/lamagraph-compiler/test/parserGolden/source/Opens.lml new file mode 100644 index 0000000..a1a1171 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/Opens.lml @@ -0,0 +1,5 @@ +module Opens + +open M1 + +open Stdlib.Fun.Abs diff --git a/lamagraph-compiler/test/parserGolden/source/Types.lml b/lamagraph-compiler/test/parserGolden/source/Types.lml new file mode 100644 index 0000000..991b7f9 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/Types.lml @@ -0,0 +1,21 @@ +module Types + +type unit + +type 'a alias = int -> 'a list -> 'a + +type data = C1 of int | C2 + +type 'a list = [] of 'a | (::) of 'a * 'a list + +type ('a, 'b) either = Left of 'a | Right of 'b + +type someTuple = T of (int * int) + +type ('a, 'b, 'c) weirdTupleAlias = (('a * 'b) list -> ('c, 'b) either list) -> ('a -> 'b, 'c) either -> 'c + +type tooMuchConstrs = C1 of (int -> int list -> int either) + | C2 of (tooMuchConstrs -> int32) + | C3 of bool + +type 'a _t = int diff --git a/lamagraph-compiler/test/parserGolden/source/ValuePath.lml b/lamagraph-compiler/test/parserGolden/source/ValuePath.lml new file mode 100644 index 0000000..bc5ed36 --- /dev/null +++ b/lamagraph-compiler/test/parserGolden/source/ValuePath.lml @@ -0,0 +1,2 @@ +let x = M.y +and y = M.x diff --git a/stack.yaml b/stack.yaml index 5b64a7a..130243d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -48,6 +48,9 @@ extra-deps: - clash-prelude-hedgehog-1.8.1@sha256:9ec3aa3f8195481f5ce4942b34a49c97dd132dd1c8f1fa58aeecbd82c2602e86,1410 - concurrent-supply-0.1.8@sha256:80b658533141660818d0781b8c8fb9a8cf69b987fcfbab782dc788bfc7df4846,1627 - prettyprinter-interp-0.2.0.0@sha256:69c339a95b265dab9b3478ca19ec96952b6b472bd0ff6e2127112a9562362c1d,2086 + - alex-3.5.1.0@sha256:de553eefe0b6548a560e9d8100486310548470a403c1fa21108dd03713da5fc7,3886 + - happy-2.0.2@sha256:df315b5554b84d313ae54ad8d79dabe5a69f943f50ad5279a3009fe6a9313fc9,4995 + - happy-lib-2.0.2@sha256:4768c8c1a34d4e9aaa61fff3f7dd7cde0cd36d2130081e5acf030389ec357189,5099 # Override default flag values for local packages and extra-deps # flags: {} diff --git a/stack.yaml.lock b/stack.yaml.lock index 6c6db8d..1c1d9e1 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -46,6 +46,27 @@ packages: size: 300 original: hackage: prettyprinter-interp-0.2.0.0@sha256:69c339a95b265dab9b3478ca19ec96952b6b472bd0ff6e2127112a9562362c1d,2086 +- completed: + hackage: alex-3.5.1.0@sha256:de553eefe0b6548a560e9d8100486310548470a403c1fa21108dd03713da5fc7,3886 + pantry-tree: + sha256: c3b94e18bde058e26070d4c06c73a42734b59135443382b4f830c33f5e3e3ed0 + size: 4473 + original: + hackage: alex-3.5.1.0@sha256:de553eefe0b6548a560e9d8100486310548470a403c1fa21108dd03713da5fc7,3886 +- completed: + hackage: happy-2.0.2@sha256:df315b5554b84d313ae54ad8d79dabe5a69f943f50ad5279a3009fe6a9313fc9,4995 + pantry-tree: + sha256: 60245e900b31d670359448806f4501b2d64803665f89bebfee2868b04ada434c + size: 6282 + original: + hackage: happy-2.0.2@sha256:df315b5554b84d313ae54ad8d79dabe5a69f943f50ad5279a3009fe6a9313fc9,4995 +- completed: + hackage: happy-lib-2.0.2@sha256:4768c8c1a34d4e9aaa61fff3f7dd7cde0cd36d2130081e5acf030389ec357189,5099 + pantry-tree: + sha256: eeac85d3d57bf23de7d091c66969480ea23d1d2764f721ea16efa25789b9e610 + size: 2403 + original: + hackage: happy-lib-2.0.2@sha256:4768c8c1a34d4e9aaa61fff3f7dd7cde0cd36d2130081e5acf030389ec357189,5099 snapshots: - completed: sha256: a81fb3877c4f9031e1325eb3935122e608d80715dc16b586eb11ddbff8671ecd