Rustã¢ããã³ãã«ã¬ã³ãã¼ï¼ãã®1ï¼3æ¥ç®ã®è¨äºã§ãã
id:udzura ã¨ç³ãã¾ããRustã¯2021å¹´ã«ãªãã¨åæã«å§ããã®ã§ããã¾ã çµé¨1å¹´æªæºã§ãããåè¶ãªãããä»åã¢ããã³ãã«ã¬ã³ãã¼ã«åå ãããã¾ãã
ä»åã¯ã¿ã¤ãã«ã®éããRustã§Crafting Interpretersãé²ãã¦ãã話ã§ããCrafting Interpretersã¨ã¯ãLoxã¨åä»ããããã¤ã³ã¿ããªã¿è¨èªãJavaãCã§å®è£ ãã¦ããããã®éç¨ã§è¨èªå®è£ ã®èè¦ãå¦ã¶ãã¨ãã§ããã¨ããæ¸ç±ã§ããWebçã¯ç¡æã§é²è¦§ã§ãã¾ãã
ç¾å¨IIç« ã¾ã§çµãããå®è£ ã¯GitHubã«ã¢ãããã¦ãã¾ãã Loxæ¬ä½ã®ã½ã¼ã¹ã³ã¼ãé¨å ã¨åãMITã©ã¤ã»ã³ã¹ã«ãã¦ãã¾ãã
ãã®è¨äºã§ã¯ãé²ããä¸ã§å·¥å¤«ããç¹ãé å¼µã£ãç¹ãå°å¦çã®æ¥è¨ã®ããã«æ®ãã¦ããã¾ã...ã
- Visitorãã¿ã¼ã³ã®å®è£
- Object ã¸ã®å¯¾å¦
- Callableãã¬ã¤ãã¸ã®ã¢ãããã£ã¹ã
- è¨èªç°å¢ã®ãã¹ã
- ãã®ä»
- å ¨ä½ã®ææ³
Visitorãã¿ã¼ã³ã®å®è£
ã¾ããIIç« ã®å®è£ ã®å ã®ã³ã¼ãã¯Javaã§ãããªã®ã§OOPããããã¿ã¼ã³ãããã¤ãåºã¦ãã¾ããVisitorãã¿ã¼ã³ã¯ãµã¤ãã®ã³ã³ãã¤ã©ãä½ãããªã©ã«ãåºã¦ãã¦ãããè¨èªå®è£ ã§ASTã辿ã£ã¦ãããããªå ´é¢ã§ããè¦ãããããã§ãã
Rustã«ããã¦ã¯ãã¾ããExprãStmtã表ç¾ããã¿ãã«åãä½ãã
pub type ExprP = Box<Expr>; pub type ExprV = Vec<Expr>; // ... #[derive(Debug, Clone)] pub struct Assign(pub TokenP, pub ExprP); #[derive(Debug, Clone)] pub struct Binary(pub ExprP, pub TokenP, pub ExprP); #[derive(Debug, Clone)] pub struct Call(pub ExprP, pub TokenP, pub ExprV); #[derive(Debug, Clone)] pub struct Get(pub ExprP, pub TokenP); //...
ãã®ã¿ãã«ãå ã¿è¾¼ãenumãä½æãã¾ããã
#[derive(Debug, Clone)] #[non_exhaustive] pub enum Expr { Assign_(Assign), Binary_(Binary), Call_(Call), Get_(Get), Grouping_(Grouping), Literal_(Lit), Logical_(Logical), Set_(Set), Super_(Super), This_(This), Unary_(Unary), Variable_(Variable), Null, Dummy, }
Visitorã¯traitã¨é¢é£åã§å®ç¾ããããããã®é¢æ°ã®å¼æ°ã«ã¯enum Exprã§ã¯ãªããåã解ããå¾ã®ã¿ãã«ãåãããã«ãã¾ããã
pub trait ExprVisitor { type R; fn visit_assign(&mut self, expr: &Assign) -> Self::R; fn visit_binary(&mut self, expr: &Binary) -> Self::R; fn visit_call(&mut self, expr: &Call) -> Self::R; fn visit_get(&mut self, expr: &Get) -> Self::R; fn visit_grouping(&mut self, expr: &Grouping) -> Self::R; fn visit_literal(&mut self, expr: &Lit) -> Self::R; fn visit_logical(&mut self, expr: &Logical) -> Self::R; fn visit_set(&mut self, expr: &Set) -> Self::R; fn visit_super(&mut self, expr: &Super) -> Self::R; fn visit_this(&mut self, expr: &This) -> Self::R; fn visit_unary(&mut self, expr: &Unary) -> Self::R; fn visit_variable(&mut self, expr: &Variable) -> Self::R; fn visit_null(&mut self) -> Self::R; } // match æã§ç°¡åã«åãåºãã impl Expr { pub fn accept<T>(&self, visitor: &mut T) -> <T as ExprVisitor>::R where T: ExprVisitor, { use Expr::*; match self { Assign_(expr) => visitor.visit_assign(expr), Binary_(expr) => visitor.visit_binary(expr), Call_(expr) => visitor.visit_call(expr), Get_(expr) => visitor.visit_get(expr), Grouping_(expr) => visitor.visit_grouping(expr), Literal_(expr) => visitor.visit_literal(expr), Logical_(expr) => visitor.visit_logical(expr), Set_(expr) => visitor.visit_set(expr), Super_(expr) => visitor.visit_super(expr), This_(expr) => visitor.visit_this(expr), Unary_(expr) => visitor.visit_unary(expr), Variable_(expr) => visitor.visit_variable(expr), Null => visitor.visit_null(), _ => panic!("[BUG] invalid type of expr."), } } }
ãã®æ¹éã§IIç« ã®å®è£ ãå®èµ°ã§ããã®ã§ãæªãã¯ãªãã£ãã®ã§ã¯ãªããã¨æãã¾ãã
ãã ãå
ã®Javaã®ã³ã¼ãã¯ãã®é¨åã¯ã³ã¼ãçæã§ãRust移æ¤çã§ãå®è£
ãé²ãã«ã¤ãExprã®ã¿ã¤ãã®è¿½å ã«ä¼´ãæãå ããç®æãå¢ãã¦ãã¾ãã¾ãããRustçã®ã build.rs
ã§çæãã風ã«ãã¦ãè¯ãã£ãã®ããããã¯ãããããã®ããªãåããªãã®ã§ã¡ãã£ã¨...ã
Object ã¸ã®å¯¾å¦
IIç« ã§ã¯ãLoxå ã§ã®å¤ã®åããã®ã¾ãã¾Javaã®åã«å¯¾å¿ãããããªè¨è¨ææ³ã¨ãªã£ã¦ãã¾ãããããã£ã¦ãLoxã®å¤ãæ±ãç®æã§ã¯Objectãå¼æ°ã«åã£ããè¿ãã¡ã½ãããè¦æè¦æã§åºã¦ããããã¢ãããã£ã¹ãããã¦ã³ãã£ã¹ããå²ã¨åºã¦ããã³ã¼ãã¨ãªãã¾ãã
Rustã§ããã¯å°ã£ãã®ã§ãããè¨èªå
é¨ã®åãAny traitã§å®ç¾ãããã㯠Value
ã¨ããåãå°å
¥ãã¦ãã¾ããã¨ã«ãã¾ããã
#[derive(Debug, Clone, PartialEq)] #[non_exhaustive] pub enum Value { // Object(Box<dyn Any>), Nil, Boolean(bool), Number(f64), LoxString(String), LoxFunction(Function), LoxClass(Class), LoxInstance(Instance), }
é¢é£ãã¦ã¨ãããããã¨ãã°ãã®ã¡ã½ããå ã®ãã®å¤æ°ã¯nullã«ãªãããããªããªãã®ãï¼ããããã¯å¤æ´ãããã®ããããªãã®ãï¼ããªã©ãèªã¿é²ãã«ã¤ãã¦å¤æããã®ã§ãå®ç¾©ãé¡ã£ã¦å¤æ´ããå¿ è¦ãåºã¦ãææ»ãã®ããã«ãªã£ã¦å°ããã¨ã¯ããã¾ãããå ã«ä¸éãå ¨ä½ãèªãã§é²ããã»ããããã£ãã®ããã
Callableãã¬ã¤ãã¸ã®ã¢ãããã£ã¹ã
Loxã§ã¯ãå¼ã³åºããã¨ãã§ãããåã¨ãã¦ãFunctionã¨Classãåå¨ãã¾ããJavaçã§ã¯ä¸¡æ¹ãLoxCallableã¤ã³ã¿ãã§ã¼ã¹ãå®è£ ãã¦ãã¾ããåæ§ã«Rustçã§ããCallableãã¬ã¤ããFunctionã¨Classã¨ããåã«å®è£ ããã¾ããã
ãããRustã¯ã¢ãããã£ã¹ããç°¡åã§ã¯ãªãã®ã§ãããããã¨ããã©ããã£ã¦å¼ã³åºãé¨åã®ã³ã¼ããåé·ã«ãªãã®ãé¿ããããã¨æ©ã¿ã¾ãããããå
±éå¦çã impl Callable
ãåãé¢æ°ã«æãåºãã°æ¸ãã¨æ°ã¥ãã¾ããã
pub trait Callable { fn arity(&self) -> u8; fn call( &self, interpreter: &mut Interpreter, arguments: &[Value], ) -> Result<Value, RuntimeBreak>; } impl ExprVisitor for Interpreter { fn visit_call(&mut self, expr: &Call) -> Self::R { let callee = self.evaluate(expr.0.as_ref())?; let mut arguments: Vec<Value> = vec![]; for v in expr.2.iter() { arguments.push(self.evaluate(v)?); } // é¢æ°å ã§é¢æ°ãå®ç¾©ããã°ä¿å®ãããã fn invoke_callable( this: &mut Interpreter, value: impl Callable, arguments: &[Value], expr: &Call, ) -> Result<Value, RuntimeBreak> { if arguments.len() != value.arity() as usize { return Err(RuntimeBreak::raise( expr.1.as_ref().to_owned(), &format!("..."), )); } value.call(this, arguments) } match callee { // Callable ãªåã®æã ããããã invoke_callable() ãå¼ã¶ Value::LoxFunction(function) => invoke_callable(self, function, &arguments, expr), Value::LoxClass(class) => invoke_callable(self, class, &arguments, expr), _ => Err(RuntimeBreak::raise( expr.1.as_ref().to_owned(), "Can only call functions and classes.", )), } } }
è¨èªç°å¢ã®ãã¹ã
ç« ãé²ãã«ã¤ããLoxã®å®è¡æç¹ã§ã®ç°å¢ï¼å¤æ°ãªã©ï¼ã®æ å ±ãä¿æãã Environment ã¨ããåãå°å ¥ãããã¨ã«ãªãã¾ããããã¯ãã親ã®ç°å¢ãã¸ã®åç §ãä¿æããæ§é ä½ã§ããèªåã®ç°å¢ã§å¤æ°ãè¦ã¤ãããªããã°è¦ªãæ¢ãã«è¡ããã¨ããç¨éãæ³å®ãã¦ãã¾ãã
#[derive(Debug, Default)] pub struct Environment { pub enclosing: Option<Rc<RefCell<Environment>>>, pub values: HashMap<String, Value>, }
ããããåã¯ä»ã®è¨èªã§ã¯ã¨ãããRustã§ã¯ãªããªã骨ãæãã¾ããç¹ã«ããç¾å¨ã®Environmentããæå®åæ°è¾¿ã£ãç°å¢ã«ãæå®ã®ååã®å¤æ°ãããããã調ã¹ãé¢æ°*1ãªã©ãå¿ è¦ã«ãªããå¦ä½ã¨ãããããã¦ããã ã unsafe æä½ããã¦ãã¾ãã
/// e.g. Environment::get_at(env, 3, "this".to_token()) impl Environment { pub fn get_at( environment: Rc<RefCell<Self>>, distance: usize, name: &Token, ) -> Result<Value, RuntimeBreak> { unsafe { &*Self::ancestor(environment, distance) }.get(name) } pub fn assign_at( environment: Rc<RefCell<Self>>, distance: usize, name: &Token, value: Value, ) -> Result<(), RuntimeBreak> { unsafe { &mut *Self::ancestor(environment, distance) }.assign(name, value) } fn ancestor(environment: Rc<RefCell<Self>>, distance: usize) -> *mut Environment { let mut environment = environment.as_ptr(); for _ in 0..distance { environment = unsafe { &*environment } .enclosing .as_ref() .expect("Too much distance") .as_ptr(); } environment } }
æ£ç´ããã¯ãã¡ããã¡ãç ®è©°ã¾ã£ã¦æ¸ãã¦ãã®ã§ãå·éã«ãªãã°ãã£ã¨ç´ ç´ã«æ¸ãããããªæ°ããã¾ãã...ã
ãã®ä»
äºç´èªãJavaã¨Rustã§å²ã¨éã£ã¦ããã®ã§ãå°å³ã«å°ããã¨ãããã¾ããã match
ã where
ãããã¯Rustã§ã®ã¿äºç´èªã§ããã
// Loxã§ã¯ããããé¢æ°ãåºã¦ãããã r#where ãæ¸ããããªã... // ã¨ãããstaticã¡ã½ãããããããå°ã... private static void report(int line, String where, String message) { System.err.println( "[line " + line + "] Error" + where + ": " + message); hadError = true; }
å ¨ä½ã®ææ³
Crafting Interpreters ã§ãã£ã¦ãããããªãä¸å®ã¬ãã«ã§ç¾å®çãªæ©è½ãæã£ãããã°ã©ãã³ã°è¨èªã®å®è£ ã¯Rustã®ææã¨ãã¦é常ã«ããã£ãããã«æãã¾ãããå ã®è¨èªãRustã§ã¯ãªãã®ã¯æ£ç´å¤§å¤ã§ããããåãªããåçµãã§ã¯ãªããã翻訳ããããå¿ è¦ããã£ãã®ã¯ãã£ããåå¼·ã«ãªã£ãããã«æãã¾ãã
è¨èªãå®è£ ããã«ã¯ãããªãã«è¤éãªãã¼ã¿æ§é ãæ±ãå¿ è¦ããããããã¯Rustã®ææ権ãåç¨ãå é¨å¯å¤æ§ãªã©ãã©ã使ã£ã¦ãããã«ã¤ãã¦ã®è¯ãç´ æã ã¨æãã¾ãã
ã¾ããç¹ã«ã¹ãã£ãããã¼ãµã®å®è£ ã«ããã¦ã¯Rustã®åã®è¡¨ç¾åã¯é常ã«å¼·åãªæ¦å¨ã ã¨æãã¾ãã*2ããã®ä»ãã¡ã¢ãªå®å ¨æ§ãé度ã¨ãã£ãå´é¢ããããRustã¯è¨èªå®è£ ã«åããè¨èªã ãªãã¨å確èªã§ãã¾ããã
å¾åã®ç« ã§ã¯ãè¨èªVMã®å°å ¥ãå«ãã ã¾ãéã£ãã¢ããã¼ãã®è¨èªå®è£ ææ³ã§é²ãããã¦ãããããªã®ã§ããã¡ãã楽ãã¿ã«ãã¦ãã¾ã*3ã
çæ§ãæ¯éCrafting Interpretersãé²ãã¦ï¼ãã¨ãä½è ãæ¯æ´ããæå³ã§ãè³¼å ¥ãã¦ï¼ã¿ãã¨è¯ããã¨æãã¾ããåçµã§ããRustã¸ã®ç¿»è¨³ã§ãããããã¯ä»ã®è¨èªã§ä½ã£ã¦ã¿ãã®ãé¢ç½ããã§ã*4ã
æå¾ã«URLãåæ²ãã¦æ¬ç¨¿ã®ç· ãã¨ããã¦ããã ãã¾ãã
*1:æ°¸ç¶ãã¼ã¿æ§é ã®å®ç¾ã®ããã§ã: https://craftinginterpreters.com/resolving-and-binding.html#persistent-environments
*2:èªè»¢è»æ¬ã«ãã¼ãµã®ç« ãããã®ãé ·ãã¾ãã
*3:ã¨ããã¤ã¤å°ãããããã¦ãã¾ã
*4:å®éåãããã«ç¿»è¨³ããã¦ãã人ã¯ä½åãããã£ãããããã§ã https://github.com/topics/craftinginterpreters