Compare commits
6 commits
9aa6bef6d9
...
2d62e8929a
Author | SHA1 | Date | |
---|---|---|---|
2d62e8929a | |||
97fdc89316 | |||
98dfbc8503 | |||
2d739d98cc | |||
61a056157b | |||
e0ab0a6c67 |
15 changed files with 286 additions and 56 deletions
|
@ -25,5 +25,14 @@ Options:
|
|||
-V, --version Print version
|
||||
```
|
||||
|
||||
For example running the
|
||||
[Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life)
|
||||
implemented in [brainfuck](https://en.wikipedia.org/wiki/Brainfuck) (read the
|
||||
source file to figure out how to use it):
|
||||
|
||||
```
|
||||
RUST_LOG=info cargo run --release -- run -s ./lox/bf.lox -- bf/game_of_life.bf
|
||||
```
|
||||
|
||||
Please note that some parts of the implementation have not been given enough
|
||||
thought.
|
||||
|
|
|
@ -4,3 +4,6 @@
|
|||
[>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>>
|
||||
]<<<
|
||||
]
|
||||
This program doesn't terminate; you will have to kill it.
|
||||
Daniel B Cristofani (cristofdathevanetdotcom)
|
||||
http://www.hevanet.com/cristofd/brainfuck/
|
||||
|
|
38
bf/game_of_life.bf
Normal file
38
bf/game_of_life.bf
Normal file
|
@ -0,0 +1,38 @@
|
|||
[life.b -- John Horton Conway's Game of Life
|
||||
(c) 2021 Daniel B. Cristofani
|
||||
http://brainfuck.org/]
|
||||
|
||||
>>>->+>+++++>(++++++++++)[[>>>+<<<-]>+++++>+>>+[<<+>>>>>+<<<-]<-]>>>>[
|
||||
[>>>+>+<<<<-]+++>>+[<+>>>+>+<<<-]>>[>[[>>>+<<<-]<]<<++>+>>>>>>-]<-
|
||||
]+++>+>[[-]<+<[>+++++++++++++++++<-]<+]>>[
|
||||
[+++++++++.-------->>>]+[-<<<]>>>[>>,----------[>]<]<<[
|
||||
<<<[
|
||||
>--[<->>+>-<<-]<[[>>>]+>-[+>>+>-]+[<<<]<-]>++>[<+>-]
|
||||
>[[>>>]+[<<<]>>>-]+[->>>]<-[++>]>[------<]>+++[<<<]>
|
||||
]<
|
||||
]>[
|
||||
-[+>>+>-]+>>+>>>+>[<<<]>->+>[
|
||||
>[->+>+++>>++[>>>]+++<<<++<<<++[>>>]>>>]<<<[>[>>>]+>>>]
|
||||
<<<<<<<[<<++<+[-<<<+]->++>>>++>>>++<<<<]<<<+[-<<<+]+>->>->>
|
||||
]<<+<<+<<<+<<-[+<+<<-]+<+[
|
||||
->+>[-<-<<[<<<]>[>>[>>>]<<+<[<<<]>-]]
|
||||
<[<[<[<<<]>+>>[>>>]<<-]<[<<<]]>>>->>>[>>>]+>
|
||||
]>+[-<<[-]<]-[
|
||||
[>>>]<[<<[<<<]>>>>>+>[>>>]<-]>>>[>[>>>]<<<<+>[<<<]>>-]>
|
||||
]<<<<<<[---<-----[-[-[<->>+++<+++++++[-]]]]<+<+]>
|
||||
]>>
|
||||
]
|
||||
|
||||
[This program simulates the Game of Life cellular automaton.
|
||||
|
||||
It duplicates the interface of the classic program at
|
||||
http://www.linusakesson.net/programming/brainfuck/index.php,
|
||||
but this program was written from scratch.
|
||||
|
||||
Type e.g. "be" to toggle the fifth cell in the second row, "q" to quit,
|
||||
or a bare linefeed to advance one generation.
|
||||
|
||||
Grid wraps toroidally. Board size in parentheses in first line (2-166 work).
|
||||
|
||||
This program is licensed under a Creative Commons Attribution-ShareAlike 4.0
|
||||
International License (http://creativecommons.org/licenses/by-sa/4.0/).]
|
|
@ -68,10 +68,10 @@ class Interpreter {
|
|||
}
|
||||
|
||||
if (instruction == ",") {
|
||||
|
||||
tape.current.data = promptAscii("> ");
|
||||
}
|
||||
|
||||
if (instruction == "[" and tape.current.data < 0.0001) {
|
||||
if (instruction == "[" and tape.current.data == 0) {
|
||||
instruction = read(this.bfpath);
|
||||
var bracketCount = 1;
|
||||
while (bracketCount > 0 and instruction != nil) {
|
||||
|
@ -81,7 +81,7 @@ class Interpreter {
|
|||
}
|
||||
}
|
||||
|
||||
if (instruction == "]" and tape.current.data >= 0.0001) {
|
||||
if (instruction == "]" and tape.current.data != 0) {
|
||||
instruction = read(this.bfpath, true);
|
||||
var bracketCount = 1;
|
||||
while (bracketCount > 0 and instruction != nil) {
|
||||
|
@ -96,5 +96,6 @@ class Interpreter {
|
|||
}
|
||||
}
|
||||
|
||||
var interpreter = Interpreter("bf/helloworld.bf");
|
||||
var bfScript = args(0);
|
||||
var interpreter = Interpreter(bfScript);
|
||||
interpreter.run();
|
||||
|
|
2
lox/read_ascii.lox
Normal file
2
lox/read_ascii.lox
Normal file
|
@ -0,0 +1,2 @@
|
|||
var a = promptAscii("> ");
|
||||
print a;
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{expression::Expression, statement::Statement, token::Literal};
|
||||
|
||||
/// Generate a string representation of a list of AST statements.
|
||||
pub fn print(statements: &[Statement]) -> String {
|
||||
let mut result = String::new();
|
||||
for statement in statements {
|
||||
|
@ -8,6 +9,7 @@ pub fn print(statements: &[Statement]) -> String {
|
|||
result
|
||||
}
|
||||
|
||||
/// Recursively generate a string representation of a single AST statement.
|
||||
fn print_statement(statement: &Statement, indent: usize) -> String {
|
||||
let indent_str = " ".repeat(indent);
|
||||
match statement {
|
||||
|
@ -93,9 +95,45 @@ fn print_statement(statement: &Statement, indent: usize) -> String {
|
|||
}
|
||||
result.trim_end().to_string()
|
||||
}
|
||||
Statement::For {
|
||||
initializer,
|
||||
condition,
|
||||
increment,
|
||||
body,
|
||||
} => {
|
||||
let mut result = format!("{}For:\n", indent_str);
|
||||
if let Some(init_stmt) = initializer {
|
||||
result.push_str(&format!(
|
||||
"{} Initializer:\n{}\n",
|
||||
indent_str,
|
||||
print_statement(init_stmt, indent + 2)
|
||||
));
|
||||
}
|
||||
if let Some(cond_expr) = condition {
|
||||
result.push_str(&format!(
|
||||
"{} Condition: {}\n",
|
||||
indent_str,
|
||||
print_expression(cond_expr)
|
||||
));
|
||||
}
|
||||
if let Some(inc_expr) = increment {
|
||||
result.push_str(&format!(
|
||||
"{} Increment: {}\n",
|
||||
indent_str,
|
||||
print_expression(inc_expr)
|
||||
));
|
||||
}
|
||||
result.push_str(&format!(
|
||||
"{} Body:\n{}",
|
||||
indent_str,
|
||||
print_statement(body, indent + 2)
|
||||
));
|
||||
result.trim_end().to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively generate a string representation of a single AST expression.
|
||||
fn print_expression(expr: &Expression) -> String {
|
||||
match expr {
|
||||
Expression::Assign { name, value } => {
|
||||
|
|
|
@ -19,6 +19,9 @@ pub struct RunConfig {
|
|||
/// Path to Lox source file.
|
||||
#[arg(short, long)]
|
||||
pub source: PathBuf,
|
||||
/// Arguments to pass to the Lox script (must be after '--').
|
||||
#[arg(last = true)]
|
||||
pub script_args: Vec<String>,
|
||||
}
|
||||
|
||||
/// Available commands for the Lox interpreter
|
||||
|
|
|
@ -60,12 +60,18 @@ pub struct Interpreter {
|
|||
/// Default configuration for the interpreter, with builtin native functions.
|
||||
impl Default for Interpreter {
|
||||
fn default() -> Self {
|
||||
Interpreter::new(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
pub fn new(cli_args: Vec<String>) -> Self {
|
||||
let env: Rc<RefCell<Environment>> = Default::default();
|
||||
|
||||
{
|
||||
// add all native functions to global environment
|
||||
let mut env = env.borrow_mut();
|
||||
for (name, fun) in native_functions::all() {
|
||||
for (name, fun) in native_functions::all(cli_args) {
|
||||
env.define(name, fun);
|
||||
}
|
||||
}
|
||||
|
@ -76,9 +82,7 @@ impl Default for Interpreter {
|
|||
locals: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
/// Execute a list of statements in sequence.
|
||||
/// Log errors but continue execution.
|
||||
pub fn run(&mut self, statements: Vec<Statement>) -> Result<(), InterpreterError> {
|
||||
|
@ -107,16 +111,17 @@ impl Interpreter {
|
|||
Statement::Print(expression) => self.print_statement(expression),
|
||||
Statement::Expression(expression) => Ok(Some(self.evaluate(expression)?)),
|
||||
Statement::Var { name, initializer } => {
|
||||
self.var_statement(name, initializer.as_ref().as_ref())
|
||||
self.var_statement(name, (**initializer).as_ref())
|
||||
}
|
||||
Statement::If {
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
} => {
|
||||
let else_branch = else_branch.as_ref().map(|x| *x.clone());
|
||||
self.if_statement(condition, then_branch, else_branch.as_ref())
|
||||
}
|
||||
} => self.if_statement(
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch.as_ref().map(|b| b.as_ref()),
|
||||
),
|
||||
Statement::While { condition, body } => self.while_statement(condition, body),
|
||||
Statement::Function { name, params, body } => {
|
||||
self.function_statement(name, params, body)
|
||||
|
@ -127,6 +132,12 @@ impl Interpreter {
|
|||
superclass,
|
||||
methods,
|
||||
} => self.class(name, superclass, methods),
|
||||
Statement::For {
|
||||
initializer,
|
||||
condition,
|
||||
increment,
|
||||
body,
|
||||
} => self.for_statement(initializer, condition, increment, body),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -449,6 +460,47 @@ impl Interpreter {
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
/// Execute a for loop.
|
||||
fn for_statement(
|
||||
&mut self,
|
||||
initializer: &Option<Box<Statement>>,
|
||||
condition: &Option<Expression>,
|
||||
increment: &Option<Expression>,
|
||||
body: &Statement,
|
||||
) -> Result<Option<Value>, InterpreterError> {
|
||||
let previous_environment = self.environment.clone();
|
||||
self.environment = Rc::new(RefCell::new(Environment::with_enclosing(
|
||||
previous_environment.clone(),
|
||||
)));
|
||||
|
||||
if let Some(init_stmt) = initializer {
|
||||
self.execute(init_stmt)?;
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut loop_condition_met = true;
|
||||
if let Some(cond_expr) = condition {
|
||||
loop_condition_met = self.evaluate(cond_expr)?.is_truthy();
|
||||
}
|
||||
|
||||
if !loop_condition_met {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(Value::Return(val)) = self.execute(body)? {
|
||||
self.environment = previous_environment;
|
||||
return Ok(Some(Value::Return(val)));
|
||||
}
|
||||
|
||||
if let Some(inc_expr) = increment {
|
||||
self.evaluate(inc_expr)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.environment = previous_environment;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Assign the value of an expression to a variable.
|
||||
fn assign(&mut self, assign_expr: &Expression) -> Result<Value, InterpreterError> {
|
||||
if let Expression::Assign { name, value } = assign_expr {
|
||||
|
@ -468,7 +520,6 @@ impl Interpreter {
|
|||
}
|
||||
Ok(evaluated_value)
|
||||
} else {
|
||||
// This should ideally not be reached if called correctly from `evaluate`
|
||||
unreachable!("Interpreter::assign called with a non-Assign expression");
|
||||
}
|
||||
}
|
||||
|
@ -479,7 +530,7 @@ impl Interpreter {
|
|||
}
|
||||
|
||||
/// Evaluate left and if the operator is Or and left is truthy or the operator is not Or and
|
||||
/// not leftis truthy short circuit and return left. Otherwise evaluate and return right.
|
||||
/// not left is truthy short circuit and return left. Otherwise evaluate and return right.
|
||||
fn logical_expression(
|
||||
&mut self,
|
||||
left: &Expression,
|
||||
|
|
|
@ -53,9 +53,9 @@ pub enum RoxError {
|
|||
}
|
||||
|
||||
/// Read the source code in a file and scan it to tokens.
|
||||
pub fn compile(source: &Path) -> Result<(), io::Error> {
|
||||
pub fn compile(source: &Path, cli_args: Vec<String>) -> Result<(), io::Error> {
|
||||
let input = fs::read_to_string(source)?;
|
||||
let mut interpreter = Interpreter::default();
|
||||
let mut interpreter = Interpreter::new(cli_args);
|
||||
|
||||
run(&input, &mut interpreter);
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ fn main() {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
if let Err(e) = rox::compile(&compile_config.source) {
|
||||
if let Err(e) = rox::compile(&compile_config.source, compile_config.script_args.clone())
|
||||
{
|
||||
error!(
|
||||
"failed to compile {}: {}",
|
||||
&compile_config.source.to_string_lossy(),
|
||||
|
|
|
@ -187,7 +187,7 @@ impl Callable for AsciiOut {
|
|||
struct PromptAscii;
|
||||
impl Callable for PromptAscii {
|
||||
fn name(&self) -> String {
|
||||
"prompt_ascii".into()
|
||||
"promptAscii".into()
|
||||
}
|
||||
|
||||
fn arity(&self) -> usize {
|
||||
|
@ -207,22 +207,64 @@ impl Callable for PromptAscii {
|
|||
.first()
|
||||
.ok_or(CallingError::CallFailed("arg not readable".into()))?;
|
||||
if let Value::String(prompt) = prompt {
|
||||
print!("{prompt} ");
|
||||
stdout().flush();
|
||||
print!("{prompt}");
|
||||
stdout()
|
||||
.flush()
|
||||
.map_err(|e| CallingError::CallFailed(e.to_string()))?;
|
||||
|
||||
let mut buffer = [0; 1];
|
||||
stdin().read_exact(&mut buffer);
|
||||
stdin()
|
||||
.read_exact(&mut buffer)
|
||||
.map_err(|e| CallingError::CallFailed(e.to_string()))?;
|
||||
|
||||
todo!()
|
||||
// Ok(Value::Number(buffer[0] as char))
|
||||
Ok(Value::Number(OrderedFloat(buffer[0] as f64)))
|
||||
} else {
|
||||
Err(CallingError::CallFailed("prompt must be a string".into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CliArgs {
|
||||
cli_args: Vec<String>,
|
||||
}
|
||||
impl Callable for CliArgs {
|
||||
fn name(&self) -> String {
|
||||
"args".into()
|
||||
}
|
||||
|
||||
fn arity(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn call(
|
||||
&self,
|
||||
_interpreter: &mut Interpreter,
|
||||
args: Vec<Value>,
|
||||
) -> Result<Value, CallingError> {
|
||||
if args.len() != self.arity() {
|
||||
return Err(CallingError::ArgumentMismatch(self.arity(), args.len()));
|
||||
}
|
||||
|
||||
let idx = args
|
||||
.first()
|
||||
.ok_or(CallingError::CallFailed("arg not readable".into()))?;
|
||||
if let Value::Number(idx) = idx {
|
||||
let idx: usize = idx.0 as usize;
|
||||
Ok(self
|
||||
.cli_args
|
||||
.get(idx)
|
||||
.map(|x| Value::String(x.clone()))
|
||||
.unwrap_or(Value::Nil))
|
||||
} else {
|
||||
Err(CallingError::CallFailed(
|
||||
"arg index must be a number".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return all native functions available to the Lox interpreter
|
||||
pub fn all() -> Vec<(String, Value)> {
|
||||
pub fn all(cli_args: Vec<String>) -> Vec<(String, Value)> {
|
||||
vec![
|
||||
(
|
||||
"asciiOut".into(),
|
||||
|
@ -241,8 +283,12 @@ pub fn all() -> Vec<(String, Value)> {
|
|||
Value::Callable((Rc::new(ReadFile {}), CallableType::Function)),
|
||||
),
|
||||
(
|
||||
"prompt".into(),
|
||||
"promptAscii".into(),
|
||||
Value::Callable((Rc::new(PromptAscii {}), CallableType::Function)),
|
||||
),
|
||||
(
|
||||
"args".into(),
|
||||
Value::Callable((Rc::new(CliArgs { cli_args }), CallableType::Function)),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
expression::Expression,
|
||||
statement::Statement,
|
||||
token::{
|
||||
self, Literal, Token,
|
||||
self, Token,
|
||||
TokenType::{self, *},
|
||||
},
|
||||
};
|
||||
|
@ -221,7 +221,7 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse a for statement by desugaring it into a while loop.
|
||||
/// Parse a for statement.
|
||||
fn for_statement(&mut self) -> Result<Statement, ParserError> {
|
||||
let line = self.current_token.line;
|
||||
self.consume(&LeftParen)
|
||||
|
@ -230,20 +230,19 @@ impl Parser {
|
|||
let initializer = if self.matches(&[Semicolon]) {
|
||||
None
|
||||
} else if self.matches(&[Var]) {
|
||||
Some(self.var_declaration()?)
|
||||
Some(Box::new(self.var_declaration()?))
|
||||
} else {
|
||||
Some(self.expression_statement()?)
|
||||
Some(Box::new(self.expression_statement()?))
|
||||
};
|
||||
|
||||
let condition = if !self.matches(&[Semicolon]) {
|
||||
self.expression()?
|
||||
let condition = if self.matches(&[Semicolon]) {
|
||||
None // No condition means it defaults to true later
|
||||
} else {
|
||||
Expression::Literal {
|
||||
value: Literal::Boolean(true),
|
||||
}
|
||||
let cond_expr = self.expression()?;
|
||||
self.consume(&Semicolon)
|
||||
.ok_or(ParserError::SemicolonAfterLoopConditionExpected(line))?;
|
||||
Some(cond_expr)
|
||||
};
|
||||
self.consume(&Semicolon)
|
||||
.ok_or(ParserError::SemicolonAfterLoopConditionExpected(line))?;
|
||||
|
||||
let increment = if !self.check(&RightParen) {
|
||||
Some(self.expression()?)
|
||||
|
@ -253,21 +252,14 @@ impl Parser {
|
|||
self.consume(&RightParen)
|
||||
.ok_or(ParserError::RightParenAfterForClausesExpected(line))?;
|
||||
|
||||
let body = self.statement()?;
|
||||
let body = match increment {
|
||||
Some(inc) => Statement::Block(vec![body, Statement::Expression(inc)]),
|
||||
None => body,
|
||||
};
|
||||
let body = Statement::While {
|
||||
condition,
|
||||
body: Box::new(body),
|
||||
};
|
||||
let body = match initializer {
|
||||
Some(initializer) => Statement::Block(vec![initializer, body]),
|
||||
None => body,
|
||||
};
|
||||
let body = Box::new(self.statement()?);
|
||||
|
||||
Ok(body)
|
||||
Ok(Statement::For {
|
||||
initializer,
|
||||
condition,
|
||||
increment,
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse an if statement with a condition, then branch, and optional else branch.
|
||||
|
@ -676,7 +668,6 @@ impl Parser {
|
|||
}
|
||||
|
||||
/// Synchronize the parser after an error by advancing to the next statement boundary.
|
||||
/// This allows parsing to continue after encountering a syntax error.
|
||||
fn synchronize(&mut self) {
|
||||
let _ = self.advance();
|
||||
while !self.is_at_end()
|
||||
|
|
|
@ -40,9 +40,13 @@ enum ClassType {
|
|||
/// Resolve variable references and perform static analysis before interpretation.
|
||||
#[derive(Debug)]
|
||||
pub struct Resolver<'a> {
|
||||
/// Stack of scopes, each mapping variable names to their initialized state.
|
||||
scopes: Vec<HashMap<String, bool>>,
|
||||
/// Reference to the interpreter to resolve variable depths.
|
||||
interpreter: &'a mut Interpreter,
|
||||
/// Track the type of the current function being resolved (regular, method, initializer, ...).
|
||||
current_fun: FunctionType,
|
||||
/// Track the type of the current class being resolved (regular, subclass, ...).
|
||||
current_class: ClassType,
|
||||
}
|
||||
|
||||
|
@ -71,7 +75,9 @@ impl<'a> Resolver<'a> {
|
|||
Statement::Block(statements) => self.resolve_block(statements),
|
||||
Statement::Print(expression) => self.resolve_expression(expression),
|
||||
Statement::Expression(expression) => self.resolve_expression(expression),
|
||||
Statement::Var { name, initializer } => self.resolve_var(name, initializer.as_ref().as_ref()),
|
||||
Statement::Var { name, initializer } => {
|
||||
self.resolve_var(name, (**initializer).as_ref())
|
||||
}
|
||||
Statement::If {
|
||||
condition,
|
||||
then_branch,
|
||||
|
@ -79,7 +85,7 @@ impl<'a> Resolver<'a> {
|
|||
} => self.resolve_if_stmt(
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch.as_ref().map(|boxed| *boxed.clone()),
|
||||
else_branch.as_ref().map(|b| b.as_ref()),
|
||||
),
|
||||
Statement::While { condition, body } => self.resolve_while_stmt(condition, body),
|
||||
Statement::Function { name, params, body } => {
|
||||
|
@ -91,6 +97,12 @@ impl<'a> Resolver<'a> {
|
|||
superclass,
|
||||
methods,
|
||||
} => self.resolve_class_stmt(name, superclass, methods),
|
||||
Statement::For {
|
||||
initializer,
|
||||
condition,
|
||||
increment,
|
||||
body,
|
||||
} => self.resolve_for_statement(initializer, condition, increment, body),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,13 +386,13 @@ impl<'a> Resolver<'a> {
|
|||
&mut self,
|
||||
condition: &Expression,
|
||||
then_branch: &Statement,
|
||||
else_branch: Option<Statement>,
|
||||
else_branch: Option<&Statement>,
|
||||
) -> Result<(), ResolverError> {
|
||||
self.resolve_expression(condition)?;
|
||||
self.resolve_statement(then_branch)?;
|
||||
|
||||
if let Some(stmt) = else_branch {
|
||||
self.resolve_statement(&stmt)?;
|
||||
if let Some(stmt_ref) = else_branch {
|
||||
self.resolve_statement(stmt_ref)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -398,6 +410,35 @@ impl<'a> Resolver<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve a for statement.
|
||||
/// This creates a new scope for the entire for construct.
|
||||
fn resolve_for_statement(
|
||||
&mut self,
|
||||
initializer: &Option<Box<Statement>>,
|
||||
condition: &Option<Expression>,
|
||||
increment: &Option<Expression>,
|
||||
body: &Statement,
|
||||
) -> Result<(), ResolverError> {
|
||||
self.begin_scope();
|
||||
|
||||
if let Some(init_stmt) = initializer {
|
||||
self.resolve_statement(init_stmt)?;
|
||||
}
|
||||
|
||||
if let Some(cond_expr) = condition {
|
||||
self.resolve_expression(cond_expr)?;
|
||||
}
|
||||
|
||||
self.resolve_statement(body)?;
|
||||
|
||||
if let Some(inc_expr) = increment {
|
||||
self.resolve_expression(inc_expr)?;
|
||||
}
|
||||
|
||||
self.end_scope();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve a function definition with parameters and body.
|
||||
/// Create a new scope for the function body and declare all parameters.
|
||||
fn resolve_function(
|
||||
|
|
|
@ -33,4 +33,10 @@ pub enum Statement {
|
|||
superclass: Option<Expression>,
|
||||
methods: Vec<Statement>,
|
||||
},
|
||||
For {
|
||||
initializer: Option<Box<Statement>>,
|
||||
condition: Option<Expression>,
|
||||
increment: Option<Expression>,
|
||||
body: Box<Statement>,
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue