SMLYACC(1)

πŸ’– SMLYACC(1) πŸ’–

okay bestie, welcome to smlyacc, from the General Commands Manual section of your friendly neighborhood man pages. this one's the parser generator for SML#, and yes she is iconic, let's get into it 🫢


✨ NAME ✨

smlyacc β€” the parser generator for SML#, periodt πŸ’…


✨ SYNOPSIS ✨

smlyacc [-s] [-p output_prefix] filename

✨ DESCRIPTION ✨

so SMLYacc is a parser generator, and she's serving major ML-Yacc energy. like, she'll accept grammar files written for ML-Yacc β€” no problem, come as you are πŸ₯° β€” BUT. and this is important bestie. the programs she generates, and the way you use them, are not compatible with the ones ML-Yacc makes. not the same vibe at all. the good news? the programs she generates can be compiled by the SML# compiler. slay.

okay so here's the default situation. for an input file X.grm, smlyacc generates:

  • X.grm.sml β€” for the generated parser
  • X.grm.sig β€” for the signature of tokens
  • and optionally X.grm.desc β€” the description of the LALR parser construction

and to compile that generated program with SML#, you have to write an interface file X.grm.smi by yourself, matching up with the generated signature X.grm.sig. yes, by hand. i know. it's giving ✨character building✨. you got this tho.


✨ OPTIONS ✨

  • -s β€” insert the token signature at the beginning of the generated .sml file, instead of making a separate .sig file. one file, no crumbs πŸŽ€
  • -p output_prefix β€” sets the prefix for the output file names. when output_prefix is X, then smlyacc generates X.sml, X.sig, and X.desc. the default is the same as the input file name, no cap πŸ’―

✨ EXAMPLES ✨

here's a minimal little example of an input file, ex.grm 🌸

%%
%term LPAREN | RPAREN | EOF
%nonterm start of word | exp of word
%pos int
%eop EOF
%name Example
%%
start : exp (exp)
exp : (0w0)
    | LPAREN exp RPAREN exp (exp1 + exp2)

and when you run this file through smlyacc, like so:

smlyacc ex.grm

you get two files back: ex.grm.sml and ex.grm.sig πŸŽ‰ only ex.grm.sml actually needs to be compiled. but to compile it, you've gotta create this ex.grm.smi file yourself, bestie:

_require "basis.smi"
_require local "ml-yacc-lib.smi"
_require local "./ex.grm.sig"
structure ExampleLrVals =
struct
  structure Parser =
  struct
    type token (= boxed)
    type stream (= ref)
    type result = word
    type pos = int
    type arg = unit
    val makeStream : {lexer : unit -> token} -> stream
    val consStream : token * stream -> stream
    val getStream : stream -> token * stream
    val sameToken : token * token -> bool
    val parse : {lookahead : int,
                 stream : stream,
                 error : string * pos * pos -> unit,
                 arg : arg}
                -> result * stream
  end
  structure Tokens =
  struct
    type pos = Parser.pos
    type token = Parser.token
    val EOF: pos * pos -> token
    val RPAREN: pos * pos -> token
    val LPAREN: pos * pos -> token
  end
end

the types of those token constructors β€” EOF, RPAREN, and LPAREN β€” get copied straight out of the generated signature file ex.grm.sig, by hand. yes, manually. iykyk 😭

so the parse function in the generated program is the parser. to invoke it you need an imperative lexer function of type unit -> token. and if you're combining with SMLLex, the lexer is generated by SMLLex. let's say SMLLex generates a lexer with this interface:

structure ExampleLex =
struct
  exception LexError
  val makeLexer : (int -> string)
                  -> unit -> ExampleLrVals.Parser.token
end

then a typical piece of code joining SMLLex and SMLYacc together looks like this, and it ate, no notes πŸ’…:

fun inputN n = TextIO.inputN (instream, n)
val lexer = ExampleLex.makeLexer inputN
val stream = ExampleLrVals.Parser.makeStream {lexer = lexer}
val (result, stream) =
    ExampleLrVals.parse
      {lookahead = 0, stream = stream,
       error = errorFn, arg = parserArg}

✨ HISTORY ✨

fun fact bestie πŸ¦‹ SMLYacc is a derivative of ML-Yacc, which was originally developed by David R. Tarditi and Andrew W. Appel. when ML-Yacc got ported over to SML#, the source code got restructured so that functor applications were replaced with SML#'s separate compilation and linking. main character rewrite energy fr fr. and for all the major changes from the original ML-Yacc, go check the SML# document πŸ‘‘


✨ SEE ALSO ✨


and that's the whole page, gorgeous 🌈 this one's from the SMLYACC(1) manual page in the General Commands Manual section β€” go forth and generate some parsers, you absolute legend πŸ’–βœ¨πŸŽ€
πŸ“¦ where this page comes from