summaryrefslogtreecommitdiffstats
path: root/Parser.sml
diff options
context:
space:
mode:
Diffstat (limited to 'Parser.sml')
-rw-r--r--Parser.sml672
1 files changed, 672 insertions, 0 deletions
diff --git a/Parser.sml b/Parser.sml
new file mode 100644
index 0000000..e464332
--- /dev/null
+++ b/Parser.sml
@@ -0,0 +1,672 @@
+infix 4 <$> <$
+infix 1 >>
+infixr 1 <|>
+infix 0 <?>
+
+structure Parser =
+struct
+ structure StringMap = Map(type k = string val cmp = String.compare)
+
+ (* vector of length 10, holding the left and right associative infix operators for each precedence level. *)
+ type infixTable = (string list * string list) vector
+ type userState = {infixTable : infixTable}
+ type sourceLoc = {file : string, row : int, column : int}
+ type state = {stream : TextIO.StreamIO.instream, loc : sourceLoc, userState : userState}
+ datatype response = Consumed | Empty
+ datatype message = Unexpected of string | Expected of string
+ type parseError = {loc : sourceLoc, msgs : message list}
+ datatype hints = Hints of string list
+ type 'a parser = state -> response * (parseError, 'a * state * hints) Result.either
+
+ val reservedWords =
+ [ "abstype", "and", "andalso", "as", "case", "datatype", "do", "else"
+ , "end", "exception", "fn", "fun", "handle", "if", "in", "infix"
+ , "infixr", "let", "local", "nonfix", "of", "op", "open", "orelse"
+ , "raise", "rec", "then", "type", "val", "with", "withtype", "while"
+ , "(", ")", "[", "]", "{", "}", ",", ":", ";", "...", "_", "|", "=", "=>", "->", "#"
+ , "eqtype", "functor", "include", "sharing", "sig"
+ , "signature", "struct", "structure", "where", ":>"
+ ]
+
+ val emptyInfixOperators : infixTable =
+ Vector.tabulate (10, fn _ => ([], []))
+
+ fun printSourceLoc ({file, row, column} : sourceLoc) : string =
+ file ^ ":" ^ Int.toString row ^ "." ^ Int.toString column
+
+ fun printError ({loc, msgs} : parseError) : string =
+ let
+ val unexpect = List.mapPartial (fn Unexpected x => SOME x | _ => NONE) msgs
+ val showUnexpect = case unexpect of
+ [] => ""
+ | s :: _ => "unexpected " ^ s ^ ";\n"
+ val expect = List.mapPartial (fn Expected s => SOME s | _ => NONE) msgs
+ in
+ printSourceLoc loc ^ " Syntax error:\n"
+ ^ showUnexpect
+ ^ "expecting " ^ String.concatWith ", " expect
+ end
+
+ fun unpackParserResponse (_ : response, Result.Left err : (parseError, 'a * state * hints) Result.either) : (string, 'a) Result.either =
+ Result.Left (printError err)
+ | unpackParserResponse (_, Result.Right (a, st, _)) =
+ if TextIO.StreamIO.endOfStream (#stream st)
+ then Result.Right a
+ else Result.Left (printSourceLoc (#loc st) ^ " Syntax error: trailing characters")
+
+ fun newLoc (fileName : string) : sourceLoc = {
+ file = fileName,
+ row = 1,
+ column = 1
+ }
+
+ fun collectInfixOperators (opTable : (string list * string list) vector) : string list =
+ Vector.foldl (fn ((a, b), acc) => a @ b @ acc) [] opTable
+
+ fun infixOps (opTable : infixTable) : string list =
+ Vector.foldl (fn ((a, b), acc) => a @ b @ acc) [] opTable
+
+ fun newState (fileName : string) (fileStream : TextIO.instream) : state = {
+ stream = TextIO.getInstream fileStream,
+ loc = newLoc fileName,
+ userState = {infixTable = emptyInfixOperators}
+ }
+
+ fun updateUserState (f : userState -> userState) : userState parser =
+ fn {stream, loc, userState} =>
+ let val st' = f userState
+ in (Empty, Result.Right (st', {stream = stream, loc = loc, userState = st'}, Hints []))
+ end
+
+ val getUserState : userState parser = updateUserState (fn x => x)
+
+ fun runParser (p : 'a parser) (fileName : string) : (string, 'a) Result.either =
+ unpackParserResponse (p (newState fileName (TextIO.openIn fileName)))
+
+ fun testParser (p : 'a parser) (s : string) : 'a =
+ case unpackParserResponse (p (newState "STRING" (TextIO.openString s))) of
+ Result.Right x => x
+ | Result.Left e => raise Fail ("Parse failed: " ^ e)
+
+ fun mergeHints (Hints a) (Hints b) : hints = Hints (a @ b)
+
+ fun withHints (Hints hints) ({loc, msgs} : parseError) : parseError = {
+ loc = loc,
+ msgs = map Expected hints @ msgs
+ }
+
+ fun errToHints ({msgs, ...} : parseError) : hints = Hints (List.mapPartial (fn Expected s => SOME s | _ => NONE) msgs)
+
+ fun compareLoc (l : sourceLoc, m : sourceLoc) : order =
+ case Int.compare (#row l, #row m) of
+ EQUAL => Int.compare (#column l, #column m)
+ | ord => ord
+
+ fun mergeError (e1 : parseError) (e2 : parseError) : parseError =
+ (* pick the longest match *)
+ case compareLoc (#loc e1, #loc e2) of
+ EQUAL => {
+ loc = #loc e1,
+ msgs = #msgs e1 @ #msgs e2
+ }
+ | GREATER => e1
+ | LESS => e2
+
+ fun bind (p : 'a parser) (f : 'a -> 'b parser) : 'b parser =
+ fn st =>
+ case p st of
+ (consumed1, Result.Right (a, st', hints)) =>
+ (case (f a) st' of
+ (Consumed, Result.Right success) => (Consumed, Result.Right success)
+ | (Empty, Result.Right (b, st'', hints')) => (consumed1, Result.Right (b, st'', mergeHints hints hints'))
+ | (Consumed, Result.Left err) => (Consumed, Result.Left (withHints hints err))
+ | (Empty, Result.Left err) => (consumed1, Result.Left (withHints hints err)))
+ | (consumed, Result.Left err) => (consumed, Result.Left err)
+
+ fun (p1 : 'a parser) >> (p2 : 'b parser) : 'b parser = bind p1 (fn _ => p2)
+
+ fun (p : 'a parser) <?> (msg : string) : 'a parser =
+ fn st =>
+ case p st of
+ (consumed, Result.Right (a, st', _)) => (consumed, Result.Right (a, st', Hints [msg]))
+ | (consumed, Result.Left {loc, ...}) => (consumed, Result.Left {loc = loc, msgs = [Expected msg]})
+
+ fun (p1 : 'a parser) <|> (p2 : 'a parser) : 'a parser =
+ fn st =>
+ case p1 st of
+ (Empty, Result.Left err) =>
+ (case p2 st of
+ (Empty, Result.Right (a, st', hints)) => (Empty, Result.Right (a, st', mergeHints (errToHints err) hints))
+ | (Empty, Result.Left err') => (Empty, Result.Left (mergeError err err'))
+ | res => res)
+ | res => res
+
+ fun const (x : 'a) (st : state) = (Empty, Result.Right (x, st, Hints []))
+
+ fun (f : 'a -> 'b) <$> (p : 'a parser) : 'b parser = bind p (const o f)
+
+ fun (x : 'a) <$ (p : 'b parser) : 'a parser = (fn _ => x) <$> p
+
+ fun try (p : 'a parser) : 'a parser =
+ fn st =>
+ case p st of
+ (Consumed, Result.Left err) => (Empty, Result.Left err)
+ | res => res
+
+ fun updatePosChar ({file, row, column} : sourceLoc) (c : char) : sourceLoc =
+ case c of
+ #"\n" => {
+ file = file,
+ row = row + 1,
+ column = 1
+ }
+ | #"\t" => {
+ file = file,
+ row = row,
+ column = column + 8 - (column - 1) mod 8
+ }
+ | _ => {
+ file = file,
+ row = row,
+ column = column + 1
+ }
+
+ fun satisfy (pred : char -> bool) : char parser =
+ fn {stream, loc, userState} =>
+ case TextIO.StreamIO.input1 stream of
+ NONE => (Empty, Result.Left {loc = loc, msgs = [Expected "UNKNOWN"]})
+ | SOME (c, stream') =>
+ if pred c
+ then (Consumed, Result.Right (c, {stream = stream', loc = updatePosChar loc c, userState = userState}, Hints []))
+ else (Empty, Result.Left {loc = loc, msgs = [Expected "UNKNOWN"]})
+
+ fun parseChar (c : char) : char parser =
+ satisfy (fn c' => c = c') <?> str c
+
+ fun parseString (s : string) : string parser =
+ case explode s of
+ [] => const ""
+ | c1 :: cs => s <$ foldl (fn (c, p) => p >> parseChar c) (parseChar c1) cs <?> "'" ^ String.toString s ^ "'"
+
+ fun manyErr () = raise Fail "many is applied to a parser that accepts an empty string"
+
+ fun many (p : 'a parser) : 'a list parser =
+ fn st =>
+ let fun walk xs s' =
+ case p s' of
+ (Consumed, Result.Right (x, s'', _)) => walk (x :: xs) s''
+ | (Consumed, Result.Left err) => (Consumed, Result.Left err)
+ | (Empty, Result.Right _) => manyErr ()
+ | (Empty, Result.Left err) => (Consumed, Result.Right (rev xs, s', errToHints err))
+ in
+ case p st of
+ (Consumed, Result.Right (x, s', _)) => walk [x] s'
+ | (Consumed, Result.Left err) => (Consumed, Result.Left err)
+ | (Empty, Result.Right _) => manyErr ()
+ | (Empty, Result.Left err) => (Empty, Result.Right ([], st, errToHints err))
+ end
+
+ fun many1 (p : 'a parser) : 'a list parser =
+ bind p (fn x =>
+ bind (many p) (fn xs =>
+ const (x :: xs)))
+
+ val space : char parser = satisfy Char.isSpace <?> "space"
+
+ val spaces : unit parser = () <$ many space <?> "white space"
+
+ fun unexpected (s : string) : 'a parser =
+ fn {loc, ...} => (Empty, Result.Left {loc = loc, msgs = [Unexpected s]})
+
+ val letter : char parser = satisfy Char.isAlpha <?> "letter"
+
+ val alphaNum : char parser = satisfy Char.isAlphaNum <?> "letter or digit"
+
+ fun oneOf ([] : char list) : char parser = raise Fail "oneOf empty"
+ | oneOf (x :: xs) = foldl (fn (c, p) => p <|> parseChar c) (parseChar x) xs
+
+ (* some day, whiteSpace will support comments *)
+ val whiteSpace = spaces
+
+ fun lexeme (p : 'a parser) : 'a parser =
+ bind p (fn x =>
+ whiteSpace >>
+ const x)
+
+ val alphaNumIdentifierLetter : char parser =
+ alphaNum <|> oneOf [#"'", #"_"]
+
+ val symbolicIdentifierLetters : char list =
+ [ #"!", #"%", #"&", #"$", #"#", #"+", #"-", #"/", #":", #"<"
+ , #"=", #">", #"?", #"@", #"\\", #"~", #"`", #"^", #"|", #"*"
+ ]
+
+ val symbolicIdentifierLetter : char parser = oneOf symbolicIdentifierLetters
+
+ val alphaNumIdentifier : string parser =
+ lexeme
+ (bind letter (fn firstLetter =>
+ bind (many alphaNumIdentifierLetter) (fn rest =>
+ const (implode (firstLetter :: rest)))))
+
+ val tyvar : string parser =
+ lexeme
+ (bind (parseChar #"'") (fn firstLetter =>
+ bind (many alphaNumIdentifierLetter) (fn rest =>
+ const (implode (firstLetter :: rest)))))
+
+ val symbolicIdentifier : string parser =
+ lexeme
+ (implode <$> many1 symbolicIdentifierLetter)
+
+ fun notReserved (reserved : string list) : string parser =
+ try
+ (bind (alphaNumIdentifier <|> symbolicIdentifier <?> "identifier") (fn identName =>
+ if List.exists (fn n => n = identName) reserved
+ then unexpected identName
+ else const identName))
+
+ val identifier : string parser =
+ bind getUserState (fn {infixTable, ...} =>
+ notReserved (infixOps infixTable @ reservedWords))
+
+ val tycon : string parser =
+ bind getUserState (fn st =>
+ notReserved ("*" :: reservedWords))
+
+ fun sepBy1 (p : 'a parser) (sep : 'b parser) : 'a list parser =
+ bind p (fn x =>
+ bind (many (sep >> p)) (fn xs =>
+ const (x :: xs)))
+
+ fun sepBy (p : 'a parser) (sep : 'b parser) : 'a list parser =
+ sepBy1 p sep <|> const []
+
+ fun symbol (s : string) : string parser = lexeme (parseString s)
+
+ val longIdentifier : string list parser = sepBy1 identifier (symbol ".")
+
+ fun notFollowedBy (p : char parser) : unit parser =
+ bind (try p) (fn c => unexpected (str c))
+ <|> const ()
+
+ fun reserved (s : string) : string parser =
+ let
+ val start =
+ if s = ""
+ then raise Fail "reserved was called on an empty string"
+ else String.sub (s, 0)
+ val isSymbolic =
+ List.exists (fn c => c = start) symbolicIdentifierLetters
+ in
+ lexeme
+ (try
+ (parseString s >>
+ notFollowedBy (if isSymbolic then symbolicIdentifierLetter else alphaNumIdentifierLetter) >>
+ const s))
+ <?> s
+ end
+
+ val infixIdentifier : string parser =
+ bind getUserState (fn {infixTable, ...} =>
+ let val ops = map reserved (Vector.foldl (fn ((l, r), acc) => l @ r @ acc) [] infixTable)
+ in
+ case ops of
+ [] => unexpected "infix op"
+ | x :: xs => foldl op <|> x xs <?> "infix op"
+ end)
+
+ val longInfixIdentifier : string list parser =
+ bind (many (bind identifier (fn id => symbol "." >> const id))) (fn idents =>
+ bind infixIdentifier (fn ii =>
+ const (idents @ [ii])))
+
+ val digit : char parser = satisfy Char.isDigit <?> "digit"
+
+ val integer : int parser =
+ lexeme
+ (bind (many1 digit) (fn digits =>
+ const (valOf (Int.fromString (implode digits)))))
+
+ val stringInternalChar : char parser =
+ (parseString "\\" >> (#"\a" <$ parseChar #"a"
+ <|> #"\b" <$ parseChar #"b"
+ <|> #"\t" <$ parseChar #"t"
+ <|> #"\n" <$ parseChar #"n"
+ <|> #"\v" <$ parseChar #"v"
+ <|> #"\f" <$ parseChar #"f"
+ <|> #"\r" <$ parseChar #"r"
+ <|> parseChar #"\""
+ <|> parseChar #"\\") <?> "string escape")
+ <|> (satisfy (fn c => c <> #"\"" andalso c <> #"\\") <?> "string character")
+
+ val stringConstant : string parser =
+ lexeme
+ (parseString "\"" >>
+ bind (implode <$> many stringInternalChar) (fn stringContent =>
+ parseString "\"" >>
+ const stringContent))
+
+ val builtin : Syntax.expr parser =
+ reserved "__builtin" >>
+ Syntax.EBuiltin <$> stringConstant
+
+ fun between (left : 'a parser) (right : 'b parser) (p : 'c parser) : 'c parser =
+ left >>
+ bind p (fn x =>
+ right >>
+ const x)
+
+ fun parseTycons (ty : Syntax.etype) : Syntax.etype parser =
+ bind tycon (fn longtycon =>
+ parseTycons (Syntax.Tycon ([ty], longtycon)))
+ <|> const ty
+
+ val rec parseSingleType : Syntax.etype parser = fn st =>
+ (Syntax.Tyvar <$> (tyvar <|> tycon)
+ <|> bind (between (symbol "(") (symbol ")") (sepBy1 parseType (symbol ","))) (fn types =>
+ case types of
+ [x] => const x
+ | _ => bind tycon (fn longtycon =>
+ const (Syntax.Tycon (types, longtycon))))) st
+ and parseTycon : Syntax.etype parser =
+ fn st =>
+ bind parseSingleType parseTycons st
+ and parseTupleType : Syntax.etype parser =
+ fn st =>
+ bind (sepBy1 parseTycon (reserved "*")) (fn types =>
+ const
+ (case types of
+ [ty] => ty
+ | _ => Syntax.TyTuple types)) st
+ and parseType : Syntax.etype parser =
+ fn st =>
+ bind parseTupleType (fn ty =>
+ (reserved "->" >>
+ bind parseType (fn ty' =>
+ const (Syntax.Tyfun (ty, ty'))))
+ <|> const ty) st
+
+ fun leftOp (i : int) : string parser =
+ bind getUserState (fn {infixTable, ...} =>
+ let val (leftOps, _) = Vector.sub (infixTable, i)
+ in
+ case (map reserved leftOps) of
+ [] => unexpected "left-associative operator"
+ | op1 :: ops => (fn x => x) <$> foldl op <|> op1 ops
+ end)
+
+ fun rightOp (i : int) : string parser =
+ bind getUserState (fn {infixTable, ...} =>
+ let val (_, rightOps) = Vector.sub (infixTable, i)
+ in
+ case (map reserved rightOps) of
+ [] => unexpected "right-associative operator"
+ | op1 :: ops => (fn x => x) <$> foldl op <|> op1 ops
+ end)
+
+ val rec atpat : Syntax.pat parser = fn st =>
+ (Syntax.PWild <$ reserved "_"
+ <|> Syntax.PInt <$> integer
+ <|> bind (between (symbol "(") (symbol ")") (sepBy pat (symbol ","))) (fn pats =>
+ const
+ (case pats of
+ [p] => p
+ | _ => Syntax.PTuple pats))
+ <|> bind longIdentifier (fn [i] => const (Syntax.PVar i) | is => const (Syntax.PCon (is, Syntax.PTuple [])))) st
+ and appPat : Syntax.pat parser = fn st =>
+ (bind longIdentifier (fn ident =>
+ bind atpat (fn arg =>
+ const (Syntax.PCon (ident, arg)))
+ <|> const (case ident of
+ [i] => Syntax.PVar i
+ | _ => Syntax.PCon (ident, Syntax.PTuple [])))
+ <|> atpat) st
+ and pat : Syntax.pat parser = fn st =>
+ foldl
+ (fn (i, patLower) =>
+ let
+ fun patLeft pat1 =
+ bind (leftOp i) (fn opEx =>
+ bind patLower (fn pat2 =>
+ let val app = Syntax.PCon ([opEx], Syntax.PTuple [pat1, pat2])
+ in patLeft app <|> const app
+ end))
+ fun patRight pat1 =
+ bind (rightOp i) (fn opEx =>
+ bind patLower (fn pat2 =>
+ bind (patRight pat2 <|> const pat2) (fn rest =>
+ const (Syntax.PCon ([opEx], Syntax.PTuple [pat1, rest])))))
+ in
+ bind patLower (fn pat1 =>
+ patLeft pat1 <|> patRight pat1 <|> const pat1)
+ end)
+ appPat
+ (List.tabulate (10, fn i => 9 - i)) st
+
+ val rec atom : Syntax.expr parser =
+ fn st =>
+ (Syntax.EInt <$> integer
+ <|> Syntax.EStr <$> stringConstant
+ <|> Syntax.EIdent <$> longIdentifier
+ <|> (reserved "op" >> Syntax.EIdent <$> longInfixIdentifier)
+ <|> builtin
+ <|> (reserved "let" >>
+ bind (many dec) (fn decs =>
+ reserved "in" >>
+ bind expr (fn e =>
+ reserved "end" >>
+ const (Syntax.ELet (List.mapPartial (fn x => x) decs, e)))))
+ <|> (symbol "(" >>
+ bind (sepBy expr (symbol ",")) (fn exprs =>
+ symbol ")" >>
+ const
+ (case exprs of
+ [x] => x
+ | _ => Syntax.ETuple exprs)))) st
+ and appExp : Syntax.expr parser =
+ fn st =>
+ bind atom (fn e0 =>
+ foldl (fn (x, acc) => Syntax.EApp (acc, x)) e0 <$> many atom) st
+ and infixExp : Syntax.expr parser =
+ fn st =>
+ foldl
+ (fn (i, exprLower) =>
+ let
+ fun exprLeft expr1 =
+ bind (leftOp i) (fn opEx =>
+ bind exprLower (fn expr2 =>
+ let val app = Syntax.EApp (Syntax.EIdent [opEx], Syntax.ETuple [expr1, expr2])
+ in exprLeft app <|> const app
+ end))
+ fun exprRight expr1 =
+ bind (rightOp i) (fn opEx =>
+ bind exprLower (fn expr2 =>
+ bind (exprRight expr2 <|> const expr2) (fn rest =>
+ const (Syntax.EApp (Syntax.EIdent [opEx], Syntax.ETuple [expr1, rest])))))
+ in
+ bind exprLower (fn expr1 =>
+ exprLeft expr1 <|> exprRight expr1 <|> const expr1)
+ end)
+ appExp
+ (List.tabulate (10, fn i => 9 - i)) st
+ and typedExp : Syntax.expr parser =
+ fn st =>
+ bind infixExp (fn e =>
+ (reserved ":" >>
+ bind parseType (fn ty =>
+ const (Syntax.ETyped (e, ty))))
+ <|> const e) st
+ and andalsoExp : Syntax.expr parser =
+ fn st =>
+ bind typedExp (fn e1 =>
+ (reserved "andalso" >>
+ bind andalsoExp (fn e2 =>
+ const (Syntax.EAndAlso (e1, e2))))
+ <|> const e1) st
+ and orelseExpr : Syntax.expr parser =
+ fn st =>
+ bind andalsoExp (fn e1 =>
+ (reserved "orelse" >>
+ bind orelseExpr (fn e2 =>
+ const (Syntax.EOrElse (e1, e2))))
+ <|> const e1) st
+ and expr : Syntax.expr parser = fn st =>
+ ((reserved "fn" >>
+ bind pat (fn p =>
+ reserved "=>" >>
+ bind expr (fn e =>
+ const (Syntax.ELambda (p, e)))))
+ <|> (reserved "case" >>
+ bind expr (fn e =>
+ reserved "of" >>
+ bind
+ (sepBy1
+ (bind pat (fn p =>
+ reserved "=>" >>
+ bind expr (fn e =>
+ const (p, e))))
+ (reserved "|")) (fn arms =>
+ const (Syntax.ECase (e, arms)))))
+ <|> orelseExpr) st
+ and dec : Syntax.dec option parser = fn st =>
+ (bind (false <$ reserved "infix" <|> true <$ reserved "infixr") (fn direction =>
+ bind (integer <|> const 0) (fn level =>
+ bind (many1 identifier) (fn ops =>
+ updateUserState
+ (fn {infixTable} =>
+ let val (leftOps, rightOps) = Vector.sub (infixTable, level)
+ in
+ if direction
+ then {infixTable = Vector.update (infixTable, level, (leftOps, ops @ rightOps))}
+ else {infixTable = Vector.update (infixTable, level, (ops @ leftOps, rightOps))}
+ end) >>
+ const NONE)))
+ <|> (reserved "datatype" >>
+ (between (symbol "(") (symbol ")") (sepBy1 tyvar (symbol ","))
+ <|> (fn x => [x]) <$> tyvar
+ <|> const []) >>
+ bind identifier (fn name =>
+ reserved "=" >>
+ bind
+ (sepBy1
+ (bind (infixIdentifier <|> identifier) (fn con =>
+ (reserved "of" >>
+ bind parseType (fn ty =>
+ const (con, SOME ty)))
+ <|> const (con, NONE)))
+ (reserved "|")) (fn cons =>
+ const (SOME (Syntax.DDatatype (name, cons))))))
+ <|> (reserved "val" >>
+ bind (true <$ reserved "rec" <|> const false) (fn isRec =>
+ bind pat (fn p =>
+ reserved "=" >>
+ bind expr (fn e =>
+ const
+ (SOME
+ (if isRec
+ then Syntax.DValRec (p, e)
+ else Syntax.DVal (p, e)))))))
+ <|> (reserved "fun" >>
+ bind
+ (sepBy1
+ (bind (bind atpat (fn pat1 =>
+ bind infixIdentifier (fn infixOp =>
+ bind atpat (fn pat2 =>
+ const (infixOp, [Syntax.PTuple [pat1, pat2]])))
+ <|> (case pat1 of
+ Syntax.PVar name =>
+ bind (many1 atpat) (fn args =>
+ const (name, args))
+ | _ => unexpected "pattern"))) (fn (name, args) =>
+ reserved "=" >>
+ bind expr (fn body =>
+ const (name, args, body))))
+ (reserved "|")) (fn cases =>
+ let val (name, _, _) = hd cases
+ in
+ if not (List.all (fn (n, _, _) => n = name) cases)
+ then raise Fail "clauses do not all have same function name"
+ else const (SOME (Syntax.DFun (name, map (fn (_, x, y) => (x, y)) cases)))
+ end))) st
+
+ val rec strdec : Syntax.dec option parser = fn st =>
+ ((reserved "structure" >>
+ bind identifier (fn strID =>
+ reserved "=" >>
+ reserved "struct" >>
+ bind (many strdec) (fn bindings =>
+ reserved "end" >>
+ const (SOME (Syntax.DStruct (strID, List.mapPartial (fn x => x) bindings))))))
+ <|> dec) st
+
+ (* There's ambiguity between pattern variables and constructors that can only
+ * be resolved by checking for constructors in scope *)
+ fun fixPatConstructors (constructors : unit StringMap.map) (Syntax.PVar v) : Syntax.pat =
+ if isSome (StringMap.lookup v constructors)
+ then Syntax.PCon ([v], Syntax.PTuple [])
+ else Syntax.PVar v
+ | fixPatConstructors constructors (Syntax.PTuple pats) = Syntax.PTuple (map (fixPatConstructors constructors) pats)
+ | fixPatConstructors constructors (Syntax.PCon (con, arg)) = Syntax.PCon (con, fixPatConstructors constructors arg)
+ | fixPatConstructors _ pat = pat
+
+ fun findConstructors (Syntax.DDatatype (_, cases)) : string list =
+ List.mapPartial
+ (fn (constructor, NONE) => SOME constructor
+ | _ => NONE)
+ cases
+ | findConstructors _ = []
+
+ fun fixDecConstructors (constructors : unit StringMap.map) (Syntax.DVal (pat, body)) : Syntax.dec =
+ Syntax.DVal (fixPatConstructors constructors pat, fixConstructors constructors body)
+ | fixDecConstructors constructors (Syntax.DValRec (pat, body)) =
+ Syntax.DValRec (fixPatConstructors constructors pat, fixConstructors constructors body)
+ | fixDecConstructors constructors (Syntax.DFun (f, arms)) =
+ Syntax.DFun (f, map (fn (args, body) => (map (fixPatConstructors constructors) args, fixConstructors constructors body)) arms)
+ | fixDecConstructors _ (decl as Syntax.DDatatype _) = decl
+ | fixDecConstructors constructors (Syntax.DStruct (name, decs)) =
+ let
+ val constructors = ref constructors
+ val decs : Syntax.dec list =
+ map
+ (fn dec =>
+ (constructors := foldl (fn (x, acc) => StringMap.insert x () acc) (!constructors) (findConstructors dec) ;
+ fixDecConstructors (!constructors) dec))
+ decs
+ in Syntax.DStruct (name, decs)
+ end
+
+ and fixConstructors (constructors : unit StringMap.map) (Syntax.ETuple exprs) : Syntax.expr =
+ Syntax.ETuple (map (fixConstructors constructors) exprs)
+ | fixConstructors constructors (Syntax.EList exprs) =
+ Syntax.EList (map (fixConstructors constructors) exprs)
+ | fixConstructors constructors (Syntax.EApp (func, arg)) =
+ Syntax.EApp (fixConstructors constructors func, fixConstructors constructors arg)
+ | fixConstructors constructors (Syntax.ETyped (expr, ty)) =
+ Syntax.ETyped (fixConstructors constructors expr, ty)
+ | fixConstructors constructors (Syntax.EAndAlso (e1, e2)) =
+ Syntax.EAndAlso (fixConstructors constructors e1, fixConstructors constructors e2)
+ | fixConstructors constructors (Syntax.EOrElse (e1, e2)) =
+ Syntax.EOrElse (fixConstructors constructors e1, fixConstructors constructors e2)
+ | fixConstructors constructors (Syntax.ELet (decs, body)) =
+ let
+ val constructors = ref constructors
+ val decs =
+ map
+ (fn dec =>
+ (constructors := foldl (fn (x, acc) => StringMap.insert x () acc) (!constructors) (findConstructors dec) ;
+ fixDecConstructors (!constructors) dec))
+ decs
+ in Syntax.ELet (decs, fixConstructors (!constructors) body)
+ end
+ | fixConstructors constructors (Syntax.ELambda (pat, body)) =
+ Syntax.ELambda (fixPatConstructors constructors pat, fixConstructors constructors body)
+ | fixConstructors constructors (Syntax.ECase (expr, arms)) =
+ Syntax.ECase (fixConstructors constructors expr, map (fn (pat, expr) => (fixPatConstructors constructors pat, fixConstructors constructors expr)) arms)
+ | fixConstructors _ expr = expr
+
+ val program : Syntax.expr parser =
+ bind (many strdec) (fn decs =>
+ const (fixConstructors StringMap.empty (Syntax.ELet (List.mapPartial (fn x => x) decs, Syntax.EInt 0))))
+ fun parse (f : string) : (string, Syntax.expr) Result.either = runParser program f
+end