Skip to content

Commit 76f8927

Browse files
authored
generalised lookup (static) (#55)
* Implement static generalised lookup - plus add a required STG compile case - extra pre exec validation * A new //= assert op that doesn't pass through * `then` function for pipeline if
1 parent 0f0dd10 commit 76f8927

File tree

8 files changed

+113
-12
lines changed

8 files changed

+113
-12
lines changed

lib/prelude.eu

+17-4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ cat: __CAT
7373
export: :suppress }
7474
if: __IF
7575

76+
` { doc: "`then(t, f, c)` - for pipeline if: - `x? then(t, f)``"
77+
export: :suppress }
78+
then(t, f, c): if(c, t, f)
79+
7680
##
7781
## List basics
7882
##
@@ -424,21 +428,30 @@ assertions: {
424428
` { doc: "`validator(v)` - find the validator for a value `v` in its metadata" }
425429
validator(v): lookup-or(:assert, const(true), meta(v))
426430

427-
` { doc: "`check(v)` - panic if value doesn't satisfy its validator" }
428-
check(v): if(v validator(v), v, panic("assertion failed"))
431+
` { doc: "`check(v)` - true if v is valid according to assert metadata" }
432+
check(v): v validator(v)
433+
434+
` { doc: "`checked(v)` - panic if value doesn't satisfy its validator" }
435+
checked(v): if(check(v), v, panic("assertion failed"))
429436
}
430437

438+
` { doc: "`e //= v` - add metadata to check expression `e` evaluates to `v` and return whether valid"
439+
export: :suppress
440+
associates: :left
441+
precedence: :meta }
442+
(e //= v): e with-meta({ assert: __EQ(v)}) assertions.check
443+
431444
` { doc: "`e //=> v` - add metadata to assert expression `e` evaluates to `v` and return value of `e`."
432445
export: :suppress
433446
associates: :left
434447
precedence: :meta }
435-
(e //=> v): e with-meta({ assert: __EQ(v)}) assertions.check
448+
(e //=> v): e with-meta({ assert: __EQ(v)}) assertions.checked
436449

437450
` { doc: "`e //=? f` - add metadata to assert expression `e` satisfies function `f` and return value of `e`."
438451
export: :suppress
439452
associates: :left
440453
precedence: :meta }
441-
(e //=? f): e with-meta({ assert: f}) assertions.check
454+
(e //=? f): e with-meta({ assert: f}) assertions.checked
442455

443456

444457
#

src/Eucalypt/Core/Desugar.hs

+24-1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ recordImports imports =
128128
modify $ \s@TranslateState {trImports = old} -> s {trImports = old ++ imports}
129129

130130

131+
131132
-- | Process names to vars as appropriate for a context where the
132133
-- exprs will be statically bound
133134
interpretForStaticBoundContext :: [CoreExpr] -> [CoreExpr]
@@ -141,6 +142,27 @@ interpretForStaticBoundContext exprs =
141142
toVar e _ = e
142143

143144

145+
146+
-- | Process static instances of generalised lookup
147+
--
148+
-- Assumes call operator is highest precedence
149+
processStaticGenLookup :: [CoreExpr] -> [CoreExpr]
150+
processStaticGenLookup =
151+
result . head . dropWhile (not . done) . iterate stepOne . initState
152+
where
153+
initState es = ([], False, es)
154+
stepOne (o@CoreLet {}:os, False, CoreOperator _ InfixLeft _ (CoreBuiltin _ "*DOT*"):es) =
155+
(o : os, True, es)
156+
stepOne (out, False, e:es) = (e : out, False, es)
157+
stepOne (o@CoreLet {}:os, True, e:es) =
158+
(rebody o (varify e) : os, False, es)
159+
stepOne _ = error "Unhandled step while processing gen lookups"
160+
done (_, _, []) = True
161+
done _ = False
162+
result (out, _, _) = reverse out
163+
164+
165+
144166
-- | Desugar Ast op soup into core op soup (to be cooked into better
145167
-- tree later, once fixity and precedence of all ops is resolved).
146168
--
@@ -154,7 +176,8 @@ interpretForStaticBoundContext exprs =
154176
-- resolved and which are just lookup keys.
155177
translateSoup :: [Expression] -> Translate CoreExpr
156178
translateSoup items =
157-
anon CoreOpSoup . interpretForStaticBoundContext . concat <$>
179+
anon CoreOpSoup .
180+
processStaticGenLookup . interpretForStaticBoundContext . concat <$>
158181
traverse trans items
159182
where
160183
trans :: Expression -> Translate [CoreExpr]

src/Eucalypt/Core/Error.hs

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Eucalypt.Core.Pretty
1717
import Eucalypt.Core.SourceMap
1818
import Eucalypt.Core.Syn
1919
import Eucalypt.Reporting.Classes
20-
import qualified Text.PrettyPrint as T
20+
import Eucalypt.Reporting.Common
2121

2222
data CoreExpShow = forall a. Show a => CoreExpShow (CoreExp a)
2323

@@ -32,6 +32,7 @@ data CoreError
3232
| InvalidOperatorSequence CoreExpShow CoreExpShow
3333
| Bug String CoreExpShow
3434
| VerifyOperatorsFailed CoreExpShow
35+
| VerifyNamesFailed CoreExpShow
3536
| VerifyUnresolvedVar CoreBindingName
3637
| NoSource
3738

@@ -41,21 +42,23 @@ instance Show CoreError where
4142
show (InvalidOperatorOutputStack exprs) = "Invalid output stack while cooking operator soup: [" ++ intercalate "," (map (\(CoreExpShow s) -> pprint s) exprs) ++ "]"
4243
show (InvalidOperatorSequence (CoreExpShow l) (CoreExpShow r)) = "Invalid sequence of operators:" ++ pprint l ++ " " ++ pprint r
4344
show (VerifyOperatorsFailed (CoreExpShow expr)) = "Unresolved operator in " ++ pprint expr
45+
show (VerifyNamesFailed (CoreExpShow expr)) = "Found name nodes, not translated to vars:" ++ pprint expr
4446
show (VerifyUnresolvedVar name) = "Unresolved variable in " ++ name
4547
show (Bug message (CoreExpShow expr)) = "BUG! " ++ message ++ " - " ++ pprint expr
4648
show NoSource = "No source"
4749

4850
instance Exception CoreError
4951

5052
instance Reportable CoreError where
51-
report = T.text . show
53+
report = standardReport "CORE ERROR" . show
5254

5355
instance HasSourceMapIds CoreError where
5456
toSourceMapIds (MultipleErrors es) = concatMap toSourceMapIds es
5557
toSourceMapIds (TooFewOperands op) = toSourceMapIds op
5658
toSourceMapIds (InvalidOperatorOutputStack exprs) = concatMap toSourceMapIds exprs
5759
toSourceMapIds (InvalidOperatorSequence l r) = concatMap toSourceMapIds [l, r]
5860
toSourceMapIds (VerifyOperatorsFailed expr) = toSourceMapIds expr
61+
toSourceMapIds (VerifyNamesFailed expr) = toSourceMapIds expr
5962
toSourceMapIds (VerifyUnresolvedVar _) = []
6063
toSourceMapIds (Bug _ expr) = toSourceMapIds expr
6164
toSourceMapIds NoSource = []

src/Eucalypt/Core/Verify.hs

+14-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import Eucalypt.Core.Syn
1717

1818
-- | Run all the check functions throughout the tree
1919
runChecks :: Show b => CoreExp b -> [CoreError]
20-
runChecks expr = verify noSoup expr ++ map (VerifyUnresolvedVar . show) (toList expr)
20+
runChecks expr =
21+
verify cleanCore expr ++ map (VerifyUnresolvedVar . show) (toList expr)
22+
23+
2124

2225
-- | Apply a check function to every level in the syntax tree
2326
verify ::
@@ -46,6 +49,16 @@ verify f e@(CoreOperator _ _ _ expr) =
4649
f e ++ verify f expr
4750
verify f e = f e
4851

52+
53+
cleanCore :: Show a => CoreExp a -> [CoreError]
54+
cleanCore e = noSoup e ++ noCoreName e
55+
56+
4957
noSoup :: Show a => CoreExp a -> [CoreError]
5058
noSoup o@(CoreOpSoup _ _) = [(VerifyOperatorsFailed . CoreExpShow) o]
5159
noSoup _ = []
60+
61+
62+
noCoreName :: Show a => CoreExp a -> [CoreError]
63+
noCoreName o@CoreName{} = [(VerifyNamesFailed . CoreExpShow) o]
64+
noCoreName _ = []

src/Eucalypt/Stg/Compiler.hs

+9-2
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ compile envSize context _metaref (C.CoreApply _ f xs) =
156156
(CoreBuiltin _ n) -> ([], Ref $ Global n)
157157
(CoreVar _ a) -> ([], Ref $ context a)
158158
_ ->
159-
( [compileBinding envSize context ("<fn>", f)]
159+
( [compileBinding envSize context ("<anon>", f)]
160160
, Ref (Local $ fromIntegral envSize))
161161
acc (ps, xrs) x =
162162
case x of
@@ -172,6 +172,14 @@ compile envSize context _metaref (C.CoreApply _ f xs) =
172172
_ -> fn
173173

174174

175+
176+
-- | Compile lambda into let to allocate and return fn
177+
compile envSize context _metaref f@CoreLambda{} =
178+
let_ [compileBinding envSize context ("<anon>", f)]
179+
$ Atom $ Local $ fromIntegral envSize
180+
181+
182+
175183
compile envSize context _metaref (CoreLookup _ obj nm) =
176184
let_
177185
[compileBinding envSize context ("", obj)]
@@ -191,7 +199,6 @@ compile envSize context _ (CoreOperator _ _x _p expr) = compile envSize context
191199
compile _ _ _ CoreName{} = error "Cannot compile name"
192200
compile _ _ _ CoreArgTuple{} = error "Cannot compile arg tuple"
193201
compile _ _ _ CoreOpSoup{} = error "Cannot compile op soup"
194-
compile _ _ _ CoreLambda{} = error "Cannot compile lambda"
195202

196203
-- | An empty context with no Refs for any Var
197204
emptyContext :: (Show v, Eq v) => v -> Ref

src/Eucalypt/Syntax/ParseExpr.hs

+19-2
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,26 @@ categorisedIdentifier =
8787
"identifier"
8888

8989

90+
-- | Any type of identifier
91+
categorisedNormalIdentifier :: Parser AtomicName
92+
categorisedNormalIdentifier =
93+
try (NormalName <$> normalIdentifier <|>
94+
NormalName <$> quotedIdentifier) <?>
95+
"identifier"
96+
9097

9198
-- | An identifier as an expression
9299
name :: Parser Expression
93100
name = located (EName <$> categorisedIdentifier <?> "name")
94101

95102

96103

104+
-- | A non-operator identifier as an expression
105+
nonOperatorName :: Parser Expression
106+
nonOperatorName = located (EName <$> categorisedNormalIdentifier <?> "property name")
107+
108+
109+
97110
-- ? primitives
98111
--
99112
-- We treat integers and doubles separately despite their being
@@ -161,7 +174,11 @@ tuple = parens $ expression `sepBy1` comma
161174
-- call parser identifies both the anchor and the arg tuple and
162175
-- returns both to the expression parser for inclusion in opsoup. This
163176
-- is why 'element' returns more than one expression.
164-
--
177+
--
178+
--
179+
-- Also note that instances of generalised lookup may have parentheses
180+
-- directly after a dot operator, e.g. @{a: 1}.(a+1)@. These must not
181+
-- be parsed as calls.
165182
rootCall :: Parser [Expression]
166183
rootCall = label "function call" $
167184
(\x y -> [x, y]) <$> callAnchorExpression <*> located (EApplyTuple <$> tuple)
@@ -174,7 +191,7 @@ call :: Parser [Expression]
174191
call = foldl invocation <$> rootCall <*> many tuple
175192

176193
callAnchorExpression :: Parser Expression
177-
callAnchorExpression = try atom <|> parenExpression
194+
callAnchorExpression = try nonOperatorName <|> parenExpression
178195

179196
listify :: Parser a -> Parser [a]
180197
listify = fmap (: [])

test/Eucalypt/Core/DesugarSpec.hs

+18
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,24 @@ soupSpec =
103103
, Syn.lookupOp
104104
, Syn.corename 3 "z"
105105
]
106+
it "handles static generalised lookup" $
107+
processStaticGenLookup
108+
[ ASyn.letexp [("a", ASyn.int 1)] $
109+
ASyn.block [ASyn.element "a" (ASyn.var "a")]
110+
, Syn.lookupOp
111+
, ASyn.soup [ASyn.corename "a", ASyn.corename "+", ASyn.corename "a"]
112+
] `shouldBe`
113+
[ ASyn.letexp [("a", ASyn.int 1)] $
114+
ASyn.soup [ASyn.corename "a", ASyn.corename "+", ASyn.corename "a"]
115+
]
116+
it "handles simple lookup on blocks" $
117+
(processStaticGenLookup . interpretForStaticBoundContext)
118+
[ ASyn.letexp [("a", ASyn.int 1)] $
119+
ASyn.block [ASyn.element "a" (ASyn.var "a")]
120+
, Syn.lookupOp
121+
, ASyn.corename "a"
122+
] `shouldBe`
123+
[ASyn.letexp [("a", ASyn.int 1)] $ ASyn.var "a"]
106124

107125
blockSpec :: Spec
108126
blockSpec =

test/Eucalypt/Syntax/ParseExprSpec.hs

+7
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,13 @@ expressionSpec =
179179
, applyTuple [normalName "b"]
180180
, applyTuple [normalName "c"]
181181
]
182+
it "does not confuse gen lookup with call syntax" $
183+
testParse expression "{ a: x }.(a + a)" `shouldParse`
184+
opsoup
185+
[ block [bare $ prop "a" $ normalName "x"]
186+
, operatorName "."
187+
, opsoupParens [normalName "a", operatorName "+", normalName "a"]
188+
]
182189
it "rejects lonely colons" $ do
183190
testParse expression `shouldFailOn` "a : a"
184191
testParse expression `shouldFailOn` "a:"

0 commit comments

Comments
 (0)