{"id":184221,"date":"2024-12-11T14:32:29","date_gmt":"2024-12-11T14:32:29","guid":{"rendered":"https:\/\/innovationspace.ansys.com\/knowledge\/?post_type=topic&#038;p=184221"},"modified":"2026-06-01T12:40:23","modified_gmt":"2026-06-01T12:40:23","slug":"advent-of-code-string-parsing-with-scade-one-state-machines","status":"publish","type":"topic","link":"https:\/\/innovationspace.ansys.com\/knowledge\/forums\/topic\/advent-of-code-string-parsing-with-scade-one-state-machines\/","title":{"rendered":"Advent of Code &#8211; String parsing with Scade One state machines"},"content":{"rendered":"<h3  id=\"ADVENT-OF-CODE\">Advent of Code<\/h3>\n<p><a href=\"https:\/\/adventofcode.com\/\">Advent of Code<\/a> is an advent calendar of small programming puzzles made by <a href=\"https:\/\/was.tl\/\">Eric Wastl<\/a>. Every day in December, it presents you with a small riddle, to be solved with code in the language of your choice.<\/p>\n<p>In this article, we&#8217;re going to look at solving one of this year&#8217;s puzzles with Scade One.<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-banner-scaled.jpg\" style=\"max-height: 700px !important\" \/><br \/>\n    <em><\/em>\n<\/p>\n<p>&#8220;But wait!&#8221;, I hear you say. &#8220;Scade One is a specialized tool to create embedded software, surely you can&#8217;t solve general programming puzzles with it!&#8221;. Well I can, and I will, because the true magic of Christmas doesn&#8217;t only lie in writing code, but in making it run on impossibly constrained embedded hardware.<\/p>\n<h3  id=\"THE-RULES\">The rules<\/h3>\n<p>First, let&#8217;s lay down some ground rules. Scade One (and the underlying Swan programming language) <em>is meant to<\/em> create embedded software, that will run on various types of hardware, from multi-CPU boards to teeny tiny microcontrollers.<\/p>\n<p>In a typical Scade One program, any interaction with the outside world (sensors, actuators or even a file system) typically happens through pre-allocated data structures. For this reason, it doesn&#8217;t really make sense to use Scade One to read and parse the raw text puzzle input from a file. Instead, we&#8217;ll be using a small Python Extract\/Transform\/Load script to turn the data into ready-to-use Swan types and constants.<\/p>\n<p>Once we have the input data, though, all the logic will be implemented in Scade One.<\/p>\n<h3  id=\"OUR-PROBLEM\">Our problem<\/h3>\n<p>Our problem today is <a href=\"https:\/\/adventofcode.com\/2024\/day\/3\">Day 3 of Advent of Code 2024<\/a>.<\/p>\n<p>Our program receives a character string as an input. Inside this character string are instructions that multiply some numbers. Well-formed multiplication instructions are written <code>mul(a,b)<\/code>, <code>a<\/code> and <code>b<\/code> being 1-3 digit numbers.<\/p>\n<p>Our program must:<\/p>\n<ul>\n<li>Compute <strong>the sum of all well-formed <code>mul(a,b)<\/code> instructions<\/strong>.<\/li>\n<li><strong>Ignore badly-formed instructions<\/strong>, e.g. <code>mul(2,2]<\/code> or <code>mul ( 4 , 6 )<\/code>.<\/li>\n<\/ul>\n<p>We are given an example string:<\/p>\n<p><code>x<strong>mul(2,4)<\/strong>%&amp;mul[3,7]!@^do_not_<strong>mul(5,5)<\/strong>+mul(32,64]then(<strong>mul(11,8)mul(8,5)<\/strong>)<\/code><\/p>\n<p>Well-formed <code>mul(a,b)<\/code> instructions are in <strong>bold<\/strong> above and add up to a total of <code>2*4 + 5*5 + 11*8 + 8*5 = 161<\/code>.<\/p>\n<h3  id=\"MODELING-A-SOLUTION\">Modeling a solution<\/h3>\n<p>So, we want to recognize patterns in a string. The right tool for this is a <a href=\"https:\/\/en.m.wikipedia.org\/wiki\/Regular_expression\">regular expression<\/a>.<\/p>\n<p>While we don&#8217;t have regular expressions out of the box in Scade One, we have something that does the exact same job: state machines. More precisely: safe, deterministic, memory-constant state machines.<\/p>\n<p>Scade One programs use a <a href=\"https:\/\/en.m.wikipedia.org\/wiki\/Dataflow_programming\">dataflow<\/a> paradigm. This means that we&#8217;re not describing step-by-step <em>instructions<\/em>, but rather describing the <em>data<\/em> and how it is transformed (via <em>equations<\/em>). The program runs in discrete time steps called <em>cycles<\/em>. At each cycle, the model transforms its inputs to outputs. The sequencing of operations is determined by the code generator, based on static analysis of the model. As far as the model designer is concerned, two independent, parallel operations happen simultaneously (this will be important later).<\/p>\n<p>In our case, our Scade One model consumes a <em>flow<\/em> of characters, which is a sequence of <code>char<\/code> values that takes, at each cycle, the successive characters of our input string. We are looking for this progression:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-mul-state-machine-1.png\" style=\"max-height: 150px !important\" \/><br \/>\n    <em><\/em>\n<\/p>\n<p>At any point during this progression, if we find an unexpected character, we go back to the start. If we successfully make it to the closing parenthesis, we compute the multiplication and add it to a counter.<\/p>\n<p>This is easy to translate into a Swan state machine:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-mul-state-machine-swan-1.jpg\" style=\"max-height: 700px !important\" \/><br \/>\n    <em>Our Swan state machine (click to enlarge)<\/em>\n<\/p>\n<p>Our state machine has two states: <code>idle<\/code> and <code>parsingMul<\/code>. When it&#8217;s parsing, it moves through the expected progression of characters, emitting a boolean <code>ok<\/code> flag to confirm that it should continue. Should an unexpected character be encountered, the <code>ok<\/code> flag is not emitted, and the state machine goes back to <code>idle<\/code>.<\/p>\n<p>Furthermore, we maintain two variables local to the scope of the state machine: <code>operandA<\/code> and <code>operandB<\/code>, used to compute each multiplication and store them into <code>result<\/code>, the output.<\/p>\n<p>One important thing to mention here: state machines are baked into the Swan language and <strong>work out-of-the-box<\/strong>. Their implementation is <strong>safe<\/strong>, <strong>deterministic<\/strong>, and <strong>operates at constant memory<\/strong>.<\/p>\n<p>I don&#8217;t know about you, but my typical experience with state machines consists in either finding an obscure library and trying to use it correctly or implementing my own and spending more time debugging the state machine itself than my business logic. In my case at least, having safe and ready to go state machines is a relief.<\/p>\n<h3  id=\"TESTING-IT-OUT\">Testing it out<\/h3>\n<p>Now that our state machine is complete, we need to test it. For this, we use a test harness, which is what Scade One calls its automated tests. Our harness is a special diagram that exercises the operator under test (outlined in purple below) and makes assertions on some values.<\/p>\n<p>Let&#8217;s look at it in debug mode:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-part1-test-harness.jpg\" style=\"max-height: 250px !important\" \/><br \/>\n    <em><\/em>\n<\/p>\n<p>It works! You can see purple debug values that confirm our desired result of <code>161<\/code>.<\/p>\n<p>We can now apply our program to our (personalized and not posted here) ~20k character input string and successfully compute the correct answer.<\/p>\n<h3  id=\"GROWING-COMPLEXITY\">Growing complexity<\/h3>\n<p>Each Advent of Code puzzle has a second part that brings some complexity to the initial problem. Here, in addition to <code>mul(a,b)<\/code> instructions, we must detect two new ones:<\/p>\n<ul>\n<li><code>do()<\/code> <strong>enables<\/strong> future <code>mul(a,b)<\/code> instructions<\/li>\n<li><code>don't()<\/code> <strong>disables<\/strong> future <code>mul(a,b)<\/code> instructions<\/li>\n<\/ul>\n<p>Our program starts with <code>mul<\/code> instructions enabled.<\/p>\n<p>We are given a new example string:<\/p>\n<p><code>X<strong>mul(2,4)<\/strong>&amp;mul[3,7]!^<strong>don't()<\/strong>_mul(5,5)+mul(32,64](mul(11,8)un<strong>do()<\/strong>?<strong>mul(8,5)<\/strong>)<\/code><\/p>\n<p>Our program must detect the instructions in <strong>bold<\/strong> above. You can see that only two <code>mul(a,b)<\/code> instructions must be summed this time, for a total of <code>2*4 + 8*5 = 48<\/code>.<\/p>\n<p>Now, seeing as we already have a state machine, an immediate reaction may be to think about how we may make it more complex to handle this new logic. However, thanks to the synchronous nature of Scade One and to its dataflow paradigm, parallel processing is free; we can simply <strong>add another, simple state machine<\/strong> next to our existing one. <strong>Both state machines will execute in parallel, synchronously<\/strong>, and communicate using a boolean flag<strong> <\/strong>with no extra effort on our part.<\/p>\n<p>Here&#8217;s the simplified version of our new state machine:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-do-dont-state-machine-1.png\" style=\"max-height: 300px !important\" \/><br \/>\n    <em><\/em>\n<\/p>\n<p>Here again, if we encounter an unexpected character at any part of this process, we simply go back to the start.<\/p>\n<p>Here is our new state machine in Scade One:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-do-dont-state-machine-swan-1.jpg\" style=\"max-height: 800px !important\" \/><br \/>\n    <em>Our second state machine (click to enlarge)<\/em>\n<\/p>\n<p>This new state machine operates on the same principle as the previous one. Instead of integer operands, it maintains a boolean variable called <code>active<\/code> to determine whether <code>mul<\/code> expressions should be parsed. To use it in our first state machine, we simply expand the guard condition to detect our first <code>m<\/code> character:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-new-guard-1.jpg\" style=\"max-height: 100px !important\" \/><br \/>\n    <em>Our first state machine using the <code>active<\/code> flag<\/em>\n<\/p>\n<p>Here is an animated view of our two state machines working together to process each character in the flow<a href=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-xmas-tree.gif\">:<\/a><\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-dual-state-machines-1.gif\" style=\"max-height: 800px !important\" \/><br \/>\n    <em>Our two state machines working together (click to enlarge)<\/em>\n<\/p>\n<p>We write a new test harness and confirm that our two state machines work together to find our desired result of <code>48<\/code>:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-part2-test-harness.jpg\" style=\"max-height: 250px !important\" \/><br \/>\n    <em><\/em>\n<\/p>\n<p>Here again, we apply this new program to our long input string and successfully complete the puzzle! While we won&#8217;t post said string or result here (inputs are personalized and <a href=\"https:\/\/adventofcode.com\/2024\/about#faq_copying\">copyrighted<\/a>), here is a cool visualization of our program running on the ~20k character string: <\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-part2-results.jpg\" style=\"max-height: 500px !important\" \/><br \/>\n    <em><\/em>\n<\/p>\n<p>You can see that our result (yellow curve) grows steadily as parsing goes on and temporarily goes flat whenever our <code>active<\/code> flag (purple curve) is <code>false<\/code>.<\/p>\n<h3  id=\"WE-COULD-HAVE-SOLVED-THIS-WITH-A-REGEX\">We could have solved this with a regex<\/h3>\n<p>I know, right? And in some way, we did: our state machine above is exactly what a (compiled) regex would be doing to our flow of characters.<\/p>\n<p>Let me add a few reasons why our approach may be desirable:<\/p>\n<ul>\n<li>If you plan on running your program on a <strong>tiny bare-metal device<\/strong>, you may not have a regex engine available (at least not one that fits the constraints of your hardware). With our state machine above, you are given a safe framework where you can predict, at compile time, exactly how much memory and processing power your program will need.<\/li>\n<li>If you&#8217;re doing <a href=\"https:\/\/innovationspace.ansys.com\/knowledge\/forums\/topic\/scade-one-an-open-model-based-ecosystem-ready-for-mbse\/\">MBSE<\/a>, you may want to <strong>trace software requirements<\/strong>, or elements of your system architecture, to individual states \/ transitions of the state machine. This can&#8217;t be done with a fine granularity on a regex one-liner.<\/li>\n<li>If you&#8217;re building <strong>safety-critical software<\/strong>, you may want to <strong>avoid a dependency<\/strong> on a regex engine (that you&#8217;d then need to review as part of your safety certification). Having code with no external dependencies, that does exactly what you need <em>and no more<\/em>, saves effort at certification time. <\/li>\n<\/ul>\n<h3  id=\"READY-TO-TAKE-ON-THE-CHALLENGE\">Ready to take on the challenge?<\/h3>\n<p>In this article, we have seen how to use Swan state machines to process a flow of character data. We have seen that safe, deterministic, synchronous state machines are baked into the Swan language. We have also seen that parallel processing comes at no extra cognitive cost: side-by-side diagrams execute in parallel, and the code generator figures out execution order and dependencies for us.<\/p>\n<p>If you&#8217;d like to play with the model presented in this article, you may download it <a href=\"https:\/\/github.com\/ansys\/scadeone-examples\/releases\/latest\/download\/2024-12-11-advent-of-code-2024.zip\">here<\/a>. You will need Scade One 2025 R1 or later to open it. If you are a SCADE user, access Scade One Essential with your existing licenses on the <a href=\"https:\/\/download.ansys.com\">Ansys Download Portal<\/a>.<\/p>\n<p>You may also schedule a live demo of Scade One using this <a href=\"https:\/\/www.ansys.com\/products\/embedded-software\/request-assessment-scade-one\">link<\/a>.<\/p>\n<p>And of course, if you&#8217;d like to give Advent of Code a shot, go to <a href=\"https:\/\/adventofcode.com\/\">https:\/\/adventofcode.com\/<\/a>!<\/p>\n<h3  id=\"ABOUT-THE-AUTHOR\">About the author<\/h3>\n<table style=\"max-width: 1000px;border: none !important\">\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px;border: none !important\">\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2024\/12\/scade-030-author-francois.png\" style=\"max-height: 150px !important\" \/>\n        <\/td>\n<td style=\"padding: 0px 10px;min-width: 150px;border: none !important\">\n<p><strong>Fran\u00e7ois Couadau<\/strong> (<a href=\"https:\/\/www.linkedin.com\/in\/fcouadau\/\">LinkedIn<\/a>) is a Senior Product Manager at Ansys. He works on embedded HMI software aspects in the SCADE and Scade One products. His past experience includes distributed systems and telecommunications.<\/p>\n<\/td>\n<\/tr>\n<\/table>\n","protected":false},"template":"","class_list":["post-184221","topic","type-topic","status-publish","hentry"],"aioseo_notices":[],"acf":[],"custom_fields":[{"0":{"_edit_lock":["1780318489:1769"],"_edit_last":["1769"],"_aioseo_title":[null],"_aioseo_description":[null],"_aioseo_keywords":["a:0:{}"],"_aioseo_og_title":[null],"_aioseo_og_description":[null],"_aioseo_og_article_section":[""],"_aioseo_og_article_tags":["a:0:{}"],"_aioseo_twitter_title":[null],"_aioseo_twitter_description":[null],"filter_by_optics_product":["Lumerical"],"_filter_by_optics_product":["field_64fb192ba3121"],"application_name":[""],"_application_name":["field_64a80903c8e15"],"family":[""],"_family":["field_64a809229a857"],"siebel_km_number":[""],"_siebel_km_number":["field_63ecbffce60db"],"salesforce_km_number":[""],"_salesforce_km_number":["field_63ecc018e60dc"],"km_published_date":[""],"_km_published_date":["field_64c77704499dd"],"product_version":[""],"_product_version":["field_64c776cb4fd2e"],"_bbp_forum_id":["27825"],"_bbp_topic_id":["184221"],"_bbp_author_ip":["176.143.247.201"],"_bbp_last_reply_id":["0"],"_bbp_last_active_id":["184222"],"_bbp_last_active_time":["2024-12-11 14:32:29"],"_bbp_reply_count":["0"],"_bbp_reply_count_hidden":["0"],"_bbp_voice_count":["0"],"_btv_view_count":["1125"],"_bbp_likes_count":["7"]},"test":"solution"}],"_links":{"self":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics\/184221","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics"}],"about":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/types\/topic"}],"version-history":[{"count":6,"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics\/184221\/revisions"}],"predecessor-version":[{"id":198951,"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics\/184221\/revisions\/198951"}],"wp:attachment":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/media?parent=184221"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}