Cornell cs3110 - Chapter9 Lessons

news/2024/10/8 22:07:52

使用 Menhir 构建 SimPL 的编译器

Lexer and Parser 语法分析模块

Lexer, Parser, AST 是三个依次耦合的模块,可以这么描述三者的关系:

Lexer ---tokens--> Parser ---nodes--> AST

相对于上面的图像化描述,cs3110 反过来构建整个 Lexer 和 Parser 的结构

ast.ml 中,定义了 AST 上节点 node 的类型:

type bop = | Add| Mul | Leq type expr = | Var of string| Int of int | Bool of bool| Binop of bop * expr * expr | If of expr * expr * expr | Let of string * expr * expr

parser.mly 中构建的 parser 接受 TOKEN 并将其翻译成 AST 上节点

(* Link the ast and the parser *)
%{open Ast
%}(* The name of tokens *)%token <int> INT
%token <string> ID
%token TRUE
%token FALSE
%token LEQ
%token TIMES
%token PLUS
%token LPAREN
%token RPAREN
%token LET
%token EQUALS
%token IN
%token IF
%token THEN
%token ELSE
%token EOF(* Compute the precedence and associativity of the operators *)
%nonassoc IN
%nonassoc ELSE
%left LEQ
%left PLUS
%left TIMES(* Define the start symbol: where to parse the program? *)
%start <Ast.expr> prog%%prog:| e = expr EOF { e };expr: | i = INT { Int i } (* integer *)| TRUE { Bool true } (* booleans *)| FALSE { Bool false }| e1 = expr Add e2 = expr { Binop (Add, e1, e2) } (* binary operators *)| e1 = expr Mul e2 = expr { Binop (Mul, e1, e2) }| e1 = expr Leq e2 = expr { Binop (Leq, e1, e2) }| LET; x = ID; EQUALS; e1 = expr; IN; e2 = expr { Let (x, e1, e2) } (* let expressions *)| IF; e1 = expr; THEN; e2 = expr; ELSE; e3 = expr { If (e1, e2, e3) } (* if expressions *)| LPAREN; e = expr; RPAREN { e } (* parentheses *);

lexer.mll 接受输入的 token 序列(也即从字符串角度看代码)并将其进行划分,返回 parser.mly 中早就定义好的 TOKEN

{open Parser
}(* Regular expressions for the lexer *)
let white = [' ' '\t']+
let digit = ['0'-'9']
let int = '-'?digit+
let letter = ['a'-'z' 'A'-'Z']
let id = letter (letter|digit)*(* Define the rules of lexer *)
rule read = parse (* 'read lexbuf' reads the next token from lexbuf *)| white { read lexbuf }| "true" { TRUE }| "false" { FALSE }| "<=" { LEQ }| "*" { TIMES }| "+" { PLUS }| "(" { LPAREN }| ")" { RPAREN }| "let" { LET }| "=" { EQUALS }| "in" { IN }| "if" { IF }| "then" { THEN }| "else" { ELSE }(* 'Lexing.lexeme lexbuf' returns the string that was matched *)| id { ID (Lexing.lexeme lexbuf) }(* 'int_of_string' converts a string to an integer *)| int { INT (int_of_string (Lexing.lexeme lexbuf)) }| eof { EOF }

对于 Menhir 工具来说,这样的构建顺序是合理的;毕竟看上去 lexer.mll 中定义 TOKEN 并不是那么方便 😃

语义分析模块

考虑小步语义风格,这方便我们在后面加入类型检查:

open Astlet parse (s : string) : expr = let lexbuf = Lexing.from_string s in let ast = Parser.prog Lexer.read lexbuf inast(** [is_value e] is whether [e] is a value. *)
let is_value e = match e with | Int _ | Bool _ -> true| _ -> false(** [subst e v x] is [e{v/x}]. *)
let subst _ _ _ =failwith "See next section"(** [step] is the [-->] relation, that is, a single step ofevaluation. *)
let rec step : expr -> expr = function| Int _ | Bool _ -> failwith "Does not step"| Var _ -> failwith "Unbound variable"| Binop (bop, e1, e2) when is_value e1 && is_value e2 ->step_bop bop e1 e2| Binop (bop, e1, e2) when is_value e1 ->Binop (bop, e1, step e2)| Binop (bop, e1, e2) -> Binop (bop, step e1, e2)| Let (x, e1, e2) when is_value e1 -> subst e2 e1 x| Let (x, e1, e2) -> Let (x, step e1, e2)| If (Bool true, e2, _) -> e2| If (Bool false, _, e3) -> e3| If (Int _, _, _) -> failwith "Guard of if must have type bool"| If (e1, e2, e3) -> If (step e1, e2, e3)(** [step_bop bop v1 v2] implements the primitive operation[v1 bop v2].  Requires: [v1] and [v2] are both values. *)
and step_bop bop e1 e2 = match bop, e1, e2 with| Add, Int a, Int b -> Int (a + b)| Mult, Int a, Int b -> Int (a * b)| Leq, Int a, Int b -> Bool (a <= b)| _ -> failwith "Operator and operand type mismatch"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/69175.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Android开发:日志功能备忘

临时记一下吧,以后就直接复制粘贴这里面的好了。实现一个日志记录程序的运行状态,并且带上时间信息,可以写一个类灵活调用。 MyLog.java package com.example.networkaccessrestrictions;import static android.content.ContentValues.TAG;import android.content.Context; …

学年(2024-2025-3) 学号(20241424)《计算机基础与程序设计》第三周学习总结

学期(2024-2025-3) 学号(20241424) 《计算机基础与程序设计》第三周学习总结 作业信息 |这个作业属于([2024-2025-3-计算机基础与程序设计](https://www.cnblogs.com/rocedu/p/9577842.html#WEEK03)| |-- |-- | |这个作业要求在(2024-2025-3计算机基础与程序设计第三周作…

mysql join语法解析

MySQL 支持以下 JOIN 语法用于 SELECT 语句和多表 DELETE 和 UPDATE 语句中的 table_references 部分: table_references: 查询中涉及的一个或多个表的引用,可以是简单表名或 JOIN 表达式的组合。 escaped_table_reference [, escaped_table_reference] ...escaped_table_ref…

Tableau修改行和列的颜色

1.修改颜色 1.1 在列上右键点击设置格式1.2 修改列和角2.逆时针、由里及外依次设置格式在直条上右键

论文《Learning Properties of Ordered and Disordered Materials from Multi-fidelity Data》中的代码实现

github地址:https://github.com/materialsvirtuallab/megnet/tree/master/multifidelity#issues介绍:当前的存储库利用了由同一作者开发的现有MEGNET软件包,并将MEGNET功能扩展到多保真数据集的建模。该存储库将共享公开发布的多保真带隙数据,并展示了运行多保真数据集的模…

Tableau双轴

1.添加度量到行2.添加分类到列3.拖动度量到左侧利润字段处放开

Tableau文本表、直条、散点图、折线图、

1文本表 两次双击选中两个维度2.直条 两次双击依次分别选中一个度量和维度3.散点图 两次双击选中两个度量4.折线图 两次双击依次分别选中一个日期和一个度量