lexer y parser
Proyecto
Para utilizar lalrpop, vamos a crear un nuevo proyecto en rust.
cargo new incr
Hay que hacer unos cambios al Cargo.toml
[package]
name = "inc"
version = "0.1.0"
authors = ["Humberto Ortiz-Zuazaga <humberto.ortiz@upr.edu>"]
edition = "2018"
build = "build.rs" # LALRPOP preprocessing
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies] # <-- We added this and everything after!
lalrpop = { version = "0.19.0", features = ["lexer"] }
[dependencies]
lalrpop-util = "0.19.0"
regex = "1"
La linea de build
indica que queremos que cargo corra codigo adicional cuando
hagamos cargo build
. El archivo build.rs
contiene el codigo a correr (ver abajo).
El archivo debe estar ubicado junto con el Cargo.toml
. Luego en build-depencencies
,
indicamos que queremos lalrpop, y que lalrpop genere el lexer. Por ultimo, en
dependencies
traemos unas utilidades.
El archivo build.rs
deben crearlo con el siguente contenido:
extern crate lalrpop; fn main() { lalrpop::process_root().unwrap(); }
lexer y parser
El lexer y parser lo especificaremos en el archivo src/nums.lalrpop
:
#![allow(unused)] fn main() { use std::str::FromStr; grammar; pub Num: i64 = <s:r"[0-9]+"> => i64::from_str(s).unwrap(); }
Este archivo importa la funciones de FromStr
que utilizamos para convertir
String
a enteros. Luego declara una gramatica, que tiene un elemento publico
Num
. Si encontramos un String
que consiste de solo digitos r"[0-9]+"
entonces convertimos esa secuencia de digitos a un i64
. La expression regular
r"[0-9]+"
es utilizada para crear el lexer, y lo que va a la derecha de =>
es código de rust que corre el parser cuando reconoce el lexema de la izquierda.
main
Para terminar el compilador, vamos a crear un src/main.rs
que llame el parser
y genere el ensamblador.
#[macro_use] extern crate lalrpop_util; lalrpop_mod!(pub nums); // synthesized by LALRPOP use std::fs; use std::env; fn compile(prog : i64) -> String { format!("section .text\n\ global our_code_starts_here\n\ our_code_starts_here:\n\ \tmov RAX, {}\n\ \tret\n", prog) } fn main() -> Result<(), Box<dyn std::error::Error>> { let args: Vec<String> = env::args().collect(); let filename = &args[1]; let prog_text = fs::read_to_string(filename)?; let prog = nums::NumParser::new().parse(&prog_text); let instr = compile(prog.unwrap()); println!("{}", instr); Ok(()) }
Probando el compilador
Podemos probar el compilador corriendolo con cargo:
cargo run 123.jn