Last active
November 4, 2016 23:15
-
-
Save keigoi/ae58e1ddd9d7b156c10ceaecbaa6559b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is a sample interactive calculator built using ML-Yacc and ML-Lex. | |
(keigoi: Code is modified to compile with SML# 3.2.0 toolchain.) | |
The calculator is defined by the files | |
calc.lex (* defines lexer *) | |
calc.grm (* defines grammar *) | |
calc.sml (* defines driver function, Calc.parse *) | |
sources.cm (* cm description file *) | |
To compile this example, type | |
$ make | |
in this directory. make will invoke smllex and smlyacc to process the | |
lexer specification calc.lex and the grammar specification calc.grm | |
respectively. Then it will compile the resulting SML source files | |
calc.lex.sml | |
calc.grm.sig | |
calc.grm.sml | |
and the calc.sml file containing the driver code. | |
The end result of loading these files is a structure Calc containing a | |
top-level driver function named parse. | |
Calc.parse : unit -> unit | |
The calculator can be invoked by applying Calc.parse to the unit value. | |
- Calc.parse(); | |
1+3; | |
result = 4 | |
or, make will also generate ./calc: | |
$ ./calc | |
1+3; | |
result = 4 | |
The calculator reads a sequence of expressions from the standard input | |
and prints the value of each expression after reading the expression. | |
Expressions must be separated by semicolons. An expression is not | |
evaluated until the semicolon is encountered. The calculator | |
terminates when an end-of-file is encountered. There is no attempt to | |
fix input errors: a lexical error will cause exception LexError to be | |
raised, while a syntax error will cause ParseError to be raised. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* Sample interactive calculator for ML-Yacc *) | |
fun lookup "bogus" = 10000 | |
| lookup s = 0 | |
%% | |
%header ( | |
structure CalcLrVals | |
) | |
%footer () | |
%eop EOF SEMI | |
(* %pos declares the type of positions for terminals. | |
Each symbol has an associated left and right position. *) | |
%pos int | |
%left SUB PLUS | |
%left TIMES DIV | |
%right CARAT | |
%term ID of string | NUM of int | PLUS | TIMES | PRINT | | |
SEMI | EOF | CARAT | DIV | SUB | |
%nonterm EXP of int | START of int option | |
%name Calc | |
%subst PRINT for ID | |
%prefer PLUS TIMES DIV SUB | |
%keyword PRINT SEMI | |
%noshift EOF | |
%value ID ("bogus") | |
%verbose | |
%% | |
(* the parser returns the value associated with the expression *) | |
START : PRINT EXP (print (Int.toString EXP); | |
print "\n"; | |
SOME EXP) | |
| EXP (SOME EXP) | |
| (NONE) | |
EXP : NUM (NUM) | |
| ID (lookup ID) | |
| EXP PLUS EXP (EXP1+EXP2) | |
| EXP TIMES EXP (EXP1*EXP2) | |
| EXP DIV EXP (EXP1 div EXP2) | |
| EXP SUB EXP (EXP1-EXP2) | |
| EXP CARAT EXP (let fun e (m,0) = 1 | |
| e (m,l) = m*e(m,l-1) | |
in e (EXP1,EXP2) | |
end) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_require "basis.smi" | |
_require "ml-yacc-lib.smi" | |
_require "./calc.grm.sig" | |
structure CalcLrVals = struct | |
structure Tokens = | |
struct | |
type pos = int | |
type token (= boxed) | |
val SUB: pos * pos -> token | |
val DIV: pos * pos -> token | |
val CARAT: pos * pos -> token | |
val EOF: pos * pos -> token | |
val SEMI: pos * pos -> token | |
val PRINT: pos * pos -> token | |
val TIMES: pos * pos -> token | |
val PLUS: pos * pos -> token | |
val NUM: (int) * pos * pos -> token | |
val ID: (string) * pos * pos -> token | |
end | |
structure ParserData = struct | |
structure LrParser = | |
struct | |
structure Token = | |
struct | |
val sameToken : Tokens.token * Tokens.token -> bool | |
end | |
structure Stream = | |
struct | |
type tok = Tokens.token | |
type stream (= boxed) | |
val streamify : (unit -> tok) -> stream | |
val cons : tok * stream -> stream | |
val get : stream -> tok * stream | |
end | |
end | |
end | |
structure Parser = struct | |
type pos = int | |
type arg = unit | |
type stream = ParserData.LrParser.Stream.stream | |
val parse : | |
{arg: arg, error:string * pos * pos-> unit, lookahead: int, stream: stream} | |
-> int option * stream | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
structure Tokens = CalcLrVals.Tokens | |
type pos = int | |
type token = CalcLrVals.Tokens.token | |
type lexresult= token | |
val pos = ref 0 | |
fun eof () = Tokens.EOF(!pos,!pos) | |
fun error (e,l : int,_) = TextIO.output (TextIO.stdOut, String.concat[ | |
"line ", (Int.toString l), ": ", e, "\n" | |
]) | |
%% | |
%structure CalcLex | |
alpha=[A-Za-z]; | |
digit=[0-9]; | |
ws = [\ \t]; | |
%% | |
\n => (pos := (!pos) + 1; lex()); | |
{ws}+ => (lex()); | |
{digit}+ => (Tokens.NUM (valOf (Int.fromString yytext), !pos, !pos)); | |
"+" => (Tokens.PLUS(!pos,!pos)); | |
"*" => (Tokens.TIMES(!pos,!pos)); | |
";" => (Tokens.SEMI(!pos,!pos)); | |
{alpha}+ => (if yytext="print" | |
then Tokens.PRINT(!pos,!pos) | |
else Tokens.ID(yytext,!pos,!pos) | |
); | |
"-" => (Tokens.SUB(!pos,!pos)); | |
"^" => (Tokens.CARAT(!pos,!pos)); | |
"/" => (Tokens.DIV(!pos,!pos)); | |
"." => (error ("ignoring bad character "^yytext,!pos,!pos); | |
lex()); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_require "basis.smi" | |
_require "ml-yacc-lib.smi" | |
_require "./calc.grm.smi" | |
structure CalcLex= | |
struct | |
structure UserDeclarations = | |
struct | |
type token = CalcLrVals.Tokens.token | |
type pos = CalcLrVals.Tokens.pos | |
end | |
val makeLexer | |
: (int -> string) -> unit | |
-> UserDeclarations.token | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_require "basis.smi" | |
_require "ml-yacc-lib.smi" | |
_require "./calc.grm.smi" | |
_require "./calc.lex.smi" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* calc.sml *) | |
(* This file provides glue code for building the calculator using the | |
* parser and lexer specified in calc.lex and calc.grm. | |
*) | |
structure Calc : sig | |
val parse : unit -> unit | |
end = | |
struct | |
(* | |
* We need a function which given a lexer invokes the parser. The | |
* function invoke does this. | |
*) | |
fun invoke lexstream = | |
let fun print_error (s,i:int,_) = | |
TextIO.output(TextIO.stdOut, | |
"Error, line " ^ (Int.toString i) ^ ", " ^ s ^ "\n") | |
in CalcLrVals.Parser.parse{lookahead=0,stream=lexstream,error=print_error,arg=()} | |
end | |
(* | |
* Finally, we need a driver function that reads one or more expressions | |
* from the standard input. The function parse, shown below, does | |
* this. It runs the calculator on the standard input and terminates when | |
* an end-of-file is encountered. | |
*) | |
fun parse () = | |
let val lexer = | |
let val lexer = CalcLex.makeLexer (fn _ => | |
(case TextIO.inputLine TextIO.stdIn | |
of SOME s => s | |
| _ => "")) | |
in CalcLrVals.ParserData.LrParser.Stream.streamify lexer | |
end | |
val dummyEOF = CalcLrVals.Tokens.EOF(0,0) | |
val dummySEMI = CalcLrVals.Tokens.SEMI(0,0) | |
fun loop lexer = | |
let val (result,lexer) = invoke lexer | |
val (nextToken,lexer) = CalcLrVals.ParserData.LrParser.Stream.get lexer | |
val _ = case result | |
of SOME r => | |
TextIO.output(TextIO.stdOut, | |
"result = " ^ (Int.toString r) ^ "\n") | |
| NONE => () | |
in if CalcLrVals.ParserData.LrParser.Token.sameToken(nextToken,dummyEOF) then () | |
else loop lexer | |
end | |
in loop lexer | |
end | |
end (* structure Calc *) | |
val _ = Calc.parse() | |
val _ = print "end." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SMLYACC=smlyacc | |
SMLLEX=smllex | |
SMLSHARP=smlsharp | |
OBJS=calc.grm.o calc.lex.o calc.o | |
SMI=calc.grm.smi calc.lex.smi calc.smi | |
all: calc | |
calc: $(OBJS) $(SMI) | |
$(SMLSHARP) -o calc calc.smi | |
# Common rules | |
.SUFFIXES: | |
.SUFFIXES: .sml .o .grm .lex .smi | |
%.o: %.sml | |
$(SMLSHARP) -c $< | |
# Clean up | |
clean: | |
rm -f calc *.o calc.grm.sml calc.grm.sig calc.lex.sml calc.grm.desc | |
# Specific rules | |
calc.o: calc.grm.smi calc.grm.sig calc.lex.smi calc.smi | |
# smlyacc/smllex | |
calc.grm.sml calc.grm.sig: calc.grm | |
$(SMLYACC) calc.grm | |
calc.lex.sml: calc.lex | |
$(SMLLEX) calc.lex | |
calc.grm.o: calc.grm.sml calc.grm.smi calc.grm.sig | |
$(SMLSHARP) -c calc.grm.sml | |
calc.lex.o: calc.lex.sml calc.lex.smi calc.grm.smi calc.grm.sig | |
$(SMLSHARP) -c calc.lex.sml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SML# smlyacc/smllex calc example |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment