Skip to content

Commit

Permalink
Merge pull request #1124 from 0xPolygonMiden/andrew-support-constants…
Browse files Browse the repository at this point in the history
…-on-control-flow

Support constants in MASM `repeat` loops
  • Loading branch information
bobbinth authored Oct 30, 2023
2 parents 20054cb + 7ca450e commit d76da5d
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Expanded capabilities of the `debug` decorator. Added `debug.mem` and `debug.local` variations (#1103).
- Introduced the `emit.<event_id>` assembly instruction (#1119).
- Introduced the `procref.<proc_name>` assembly instruction (#1113).
- Added the ability to use constants as counters in `repeat` loops (#1124).

#### Stdlib
- Introduced `std::utils` module with `is_empty_word` procedure. Refactored `std::collections::smt`
Expand Down
4 changes: 3 additions & 1 deletion assembly/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ pub use invocation_target::InvocationTarget;
mod parsers;
use parsers::{parse_constants, ParserContext};

pub(crate) use parsers::{NAMESPACE_LABEL_PARSER, PROCEDURE_LABEL_PARSER};
pub(crate) use parsers::{
parse_param_with_constant_lookup, NAMESPACE_LABEL_PARSER, PROCEDURE_LABEL_PARSER,
};

mod serde;
pub use serde::AstSerdeOptions;
Expand Down
2 changes: 1 addition & 1 deletion assembly/src/ast/parsers/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ impl ParserContext<'_> {
// record start of the repeat block and consume the 'repeat' token
let repeat_start = tokens.pos();
let repeat_token = tokens.read().expect("no repeat token");
let times = repeat_token.parse_repeat()?;
let times = repeat_token.parse_repeat(&self.local_constants)?;
tokens.advance();

// read the loop body
Expand Down
2 changes: 1 addition & 1 deletion assembly/src/ast/parsers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ fn parse_const_value(

/// Parses a param from the op token with the specified type and index. If the param is a constant
/// label, it will be looked up in the provided constant map.
fn parse_param_with_constant_lookup<R>(
pub(crate) fn parse_param_with_constant_lookup<R>(
op: &Token,
param_idx: usize,
constants: &LocalConstMap,
Expand Down
32 changes: 32 additions & 0 deletions assembly/src/ast/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,38 @@ fn test_ast_module_serde_imports_not_serialized() {
assert_correct_module_serialization(source, false);
}

#[test]
fn test_repeat_with_constant_count() {
let source = "\
const.A=3
const.B=A*3+5
begin
repeat.A
push.1
end
repeat.B
push.0
end
end";

assert_correct_program_serialization(source, false);

let nodes: Vec<Node> = vec![
Node::Repeat {
times: 3,
body: CodeBody::new(vec![Node::Instruction(Instruction::PushU8(1))]),
},
Node::Repeat {
times: 14,
body: CodeBody::new(vec![Node::Instruction(Instruction::PushU8(0))]),
},
];

assert_program_output(source, BTreeMap::new(), nodes);
}

fn assert_program_output(source: &str, procedures: LocalProcMap, body: Vec<Node>) {
let program = ProgramAst::parse(source).unwrap();
assert_eq!(program.body.nodes(), body);
Expand Down
9 changes: 5 additions & 4 deletions assembly/src/tokens/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{
ast::InvocationTarget, BTreeMap, ByteReader, ByteWriter, Deserializable, DeserializationError,
LibraryPath, ParsingError, ProcedureName, Serializable, String, ToString, Vec,
ast::{parse_param_with_constant_lookup, InvocationTarget},
BTreeMap, ByteReader, ByteWriter, Deserializable, DeserializationError, LibraryPath,
ParsingError, ProcedureName, Serializable, String, ToString, Vec,
};
use core::fmt;

Expand Down Expand Up @@ -238,12 +239,12 @@ impl<'a> Token<'a> {
}
}

pub fn parse_repeat(&self) -> Result<u32, ParsingError> {
pub fn parse_repeat(&self, constants: &BTreeMap<String, u64>) -> Result<u32, ParsingError> {
assert_eq!(Self::REPEAT, self.parts[0], "not a repeat");
match self.num_parts() {
0 => unreachable!(),
1 => Err(ParsingError::missing_param(self, "repeat.<num_repetitions>")),
2 => self.parts[1].parse::<u32>().map_err(|_| ParsingError::invalid_param(self, 1)),
2 => parse_param_with_constant_lookup::<u32>(self, 1, constants),
_ => Err(ParsingError::extra_param(self)),
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs/src/user_docs/assembly/flow_control.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ end
where:

* `instructions` can be a sequence of any instructions, including nested control structures.
* `count` is the number of times the `instructions` sequence should be repeated (e.g. `repeat.10`). `count` must be an integer greater than $0$.
* `count` is the number of times the `instructions` sequence should be repeated (e.g. `repeat.10`). `count` must be an integer or a [constant](./code_organization.md#constants) greater than $0$.

> **Note**: During compilation the `repeat.<count>` blocks are unrolled and expanded into `<count>` copies of its inner block, there is no additional cost for counting variables in this case.
Expand Down

0 comments on commit d76da5d

Please sign in to comment.