Urbit Developers
  • Lightning Tutorials

    • Introduction
    • Build a Groups App
    • Build a Chat App
    • Build a Voting App
    • Core Curriculum

      • Hoon School

        • Introduction
        • 1. Hoon Syntax
        • 2. Azimuth (Urbit ID)
        • 3. Gates (Functions)
        • 4. Molds (Types)
        • 5. Cores
        • 6. Trees and Addressing
        • 7. Libraries
        • 8. Testing Code
        • 9. Text Processing I
        • 10. Cores and Doors
        • 11. Data Structures
        • 12. Type Checking
        • 13. Conditional Logic
        • 14. Subject-Oriented Programming
        • 15. Text Processing II
        • 16. Functional Programming
        • 17. Text Processing III
        • 18. Generic and Variant Cores
        • 19. Mathematics
        • App School I

          • Introduction
          • 1. Arvo
          • 2. The Agent Core
          • 3. Imports and Aliases
          • 4. Lifecycle
          • 5. Cards
          • 6. Pokes
          • 7. Structures and Marks
          • 8. Subscriptions
          • 9. Vanes
          • 10. Scries
          • 11. Failure
          • 12. Next Steps
          • Appendix: Types
          • App School II (Full-Stack)

            • Introduction
            • 1. Types
            • 2. Agent
            • 3. JSON
            • 4. Marks
            • 5. Eyre
            • 6. React app setup
            • 7. React app logic
            • 8. Desk and glob
            • 9. Summary
          • Environment Setup
          • Additional Guides

            • Hoon Workbook

              • Competitive Programming
              • Gleichniszahlenreihe
              • Rhonda Numbers
              • Roman Numerals
              • Solitaire Cipher
              • App Workbook

                • Building a CLI App
                • Debugging Wrapper
                • Host a Website
                • Serving a JS Game
                • Ship Monitoring
                • Styled Text
                • Threads

                  • Fundamentals
                  • Bind
                  • Input
                  • Output
                  • Summary
                • Aqua Tests
                • Remote Scry
                • Command-Line Apps
                • HTTP API
                • Eyre noun channels
                • JSON
                • Generators
                • Parsing Text
                • Sail (HTML)
                • Udon (Markdown-esque)
                • Software Distribution
                • Strings
                • Unit Tests
                • Vases
                Urbit Developers
                • Lightning Tutorials

                  • Introduction
                  • Build a Groups App
                  • Build a Chat App
                  • Build a Voting App
                  • Core Curriculum

                    • Hoon School

                      • Introduction
                      • 1. Hoon Syntax
                      • 2. Azimuth (Urbit ID)
                      • 3. Gates (Functions)
                      • 4. Molds (Types)
                      • 5. Cores
                      • 6. Trees and Addressing
                      • 7. Libraries
                      • 8. Testing Code
                      • 9. Text Processing I
                      • 10. Cores and Doors
                      • 11. Data Structures
                      • 12. Type Checking
                      • 13. Conditional Logic
                      • 14. Subject-Oriented Programming
                      • 15. Text Processing II
                      • 16. Functional Programming
                      • 17. Text Processing III
                      • 18. Generic and Variant Cores
                      • 19. Mathematics
                      • App School I

                        • Introduction
                        • 1. Arvo
                        • 2. The Agent Core
                        • 3. Imports and Aliases
                        • 4. Lifecycle
                        • 5. Cards
                        • 6. Pokes
                        • 7. Structures and Marks
                        • 8. Subscriptions
                        • 9. Vanes
                        • 10. Scries
                        • 11. Failure
                        • 12. Next Steps
                        • Appendix: Types
                        • App School II (Full-Stack)

                          • Introduction
                          • 1. Types
                          • 2. Agent
                          • 3. JSON
                          • 4. Marks
                          • 5. Eyre
                          • 6. React app setup
                          • 7. React app logic
                          • 8. Desk and glob
                          • 9. Summary
                        • Environment Setup
                        • Additional Guides

                          • Hoon Workbook

                            • Competitive Programming
                            • Gleichniszahlenreihe
                            • Rhonda Numbers
                            • Roman Numerals
                            • Solitaire Cipher
                            • App Workbook

                              • Building a CLI App
                              • Debugging Wrapper
                              • Host a Website
                              • Serving a JS Game
                              • Ship Monitoring
                              • Styled Text
                              • Threads

                                • Fundamentals
                                • Bind
                                • Input
                                • Output
                                • Summary
                              • Aqua Tests
                              • Remote Scry
                              • Command-Line Apps
                              • HTTP API
                              • Eyre noun channels
                              • JSON
                              • Generators
                              • Parsing Text
                              • Sail (HTML)
                              • Udon (Markdown-esque)
                              • Software Distribution
                              • Strings
                              • Unit Tests
                              • Vases
                              Guides/Additional Guides/App Workbook

                              Building a CLI App

                              We will utilize the basic calculator app logic from the parsing guide to produce a linked calculator agent %rpn supporting the following operators by the appropriate parsers:

                              • numbers (as @rs without . dot prefix) (royl-rs:so)
                              • + lus, addition (lus)
                              • - hep, subtraction (hep)
                              • * tar, multiplication (tar)
                              • / fas, division (fas)
                              • . dot, display top of stack (dot)

                              We will leave all regular Gall arms as their defaults, but of course poking, subscribing, and peeking should be supported in a full application.

                              Agent Logic

                              /sur/rpn.hoon

                              We just need to define the expected operators that will show up in the stack. These are @t text constants.

                              |%
                              +$ op $? [%op %add]
                              [%op %sub]
                              [%op %mul]
                              [%op %div]
                              [%op %sho]
                              ==
                              +$ num @rs
                              +$ command ?(@rs op)
                              --

                              (+$command doesn't really feel like the right name here, but we're pattern-matching with the demo /app/shoe.hoon.)

                              /lib/rpn.hoon

                              These are the parsing rules that the CLI agent will use. We could include these directly in the agent file but we'll post them to a library file.

                              |%
                              ++ num royl-rs:so
                              ++ op-add (cook |=(p=@ ?:(=('+' p) op+%add ~)) lus)
                              ++ op-sub (cook |=(p=@ ?:(=('-' p) op+%sub ~)) hep)
                              ++ op-mul (cook |=(p=@ ?:(=('*' p) op+%mul ~)) tar)
                              ++ op-div (cook |=(p=@ ?:(=('/' p) op+%div ~)) fas)
                              ++ op-sho (cook |=(p=@ ?:(=('.' p) op+%sho ~)) dot)
                              ++ ops ;~(pose op-add op-sub op-mul op-div op-sho)
                              --

                              /app/rpn.hoon

                              ++ state-0
                              $: %0
                              stack=(list ?(@rs op:rpn))
                              ==

                              ++command-parser

                              We want this arm to wait until RETURN is pressed so we ++stag the value with | FALSE/%.n.

                              ++ command-parser
                              |= =sole-id:shoe
                              ^+ |~(nail *(like [? command:rpn]))
                              %+ stag |
                              (cook command:rpn ;~(pose num:rpnlib ops:rpnlib))

                              ++on-command

                              This arm pushes values onto the stack, displays the stack, then checks to parse for the result of an operation.

                              ++ on-command
                              |= [=sole-id:shoe =command:rpn]
                              ^- (quip card _this)
                              =/ old-stack (weld stack ~[command])
                              =/ new-stack (process:rpnlib old-stack)
                              :_ this(stack new-stack)
                              :~ [%shoe ~ sole+klr+~[(crip "{<old-stack>} →")]]
                              [%shoe ~ sole+klr+~[[[`%br ~ `%g] (crip "{<new-stack>}") ~]]]
                              ==

                              For this we add a helper arm to /lib/rpn.hoon which takes each entry, makes sure it is a @rs atom, and carries out the operation. (This could probably be made more efficient.)

                              /lib/rpn.hoon

                              /- rpn
                              :: * * *
                              ++ process
                              |= stack=(list command:rpn)
                              ^- (list command:rpn)
                              ~| "Failure processing operation on stack {<stack>}"
                              ?~ stack !!
                              ?- `command:rpn`(snag 0 (flop stack))
                              [%op %add]
                              =/ augend ;;(@rs `command:rpn`(snag 1 (flop stack)))
                              =/ addend ;;(@rs `command:rpn`(snag 2 (flop stack)))
                              (flop (weld ~[(add:rs augend addend)] (slag 3 (flop stack))))
                              ::
                              [%op %sub]
                              =/ minuend ;;(@rs `command:rpn`(snag 1 (flop stack)))
                              =/ subtrahend ;;(@rs `command:rpn`(snag 2 (flop stack)))
                              (flop (weld ~[(sub:rs minuend subtrahend)] (slag 3 (flop stack))))
                              ::
                              [%op %mul]
                              =/ multiplicand ;;(@rs `command:rpn`(snag 1 (flop stack)))
                              =/ multiplier ;;(@rs `command:rpn`(snag 2 (flop stack)))
                              (flop (weld ~[(mul:rs multiplicand multiplier)] (slag 3 (flop stack))))
                              ::
                              [%op %div]
                              =/ numerator ;;(@rs `command:rpn`(snag 1 (flop stack)))
                              =/ denominator ;;(@rs `command:rpn`(snag 2 (flop stack)))
                              (flop (weld ~[(div:rs numerator denominator)] (slag 3 (flop stack))))
                              ::
                              [%op %sho]
                              ~& > "{<(snag 1 (flop stack))>}"
                              (flop (slag 1 (flop stack)))
                              ::
                              @rs
                              stack
                              ==

                              Linking

                              After a %sole agent has been |installed, it should be registered for Dojo to cycle input to it using |link.

                              |link %rpn

                              Now Ctrl+X allows you to switch to that app and evaluate expressions using it.

                              gall: booted %rpn
                              > 50
                              ~ →
                              ~[.50]
                              > 25
                              ~[.50] →
                              ~[.50 .25]
                              > -
                              ~[.50 .25] →
                              ~[.-25]
                              > 5
                              ~[.-25] →
                              ~[.-25 .5]
                              > /
                              ~[.-25 .5] →
                              ~[.-0.19999999]
                              > 5
                              ~[.-0.19999999] →
                              ~[.-0.19999999 .5]
                              > *
                              ~[.-0.19999999 .5] →
                              ~[.-0.99999994]
                              > 1
                              ~[.-0.99999994] →
                              ~[.-0.99999994 .1]
                              > /
                              ~[.-0.99999994 .1] →
                              ~[.-1]

                              Exercises

                              • Extend the calculator app to support modulus as % cen.
                              • Extend the calculator app so it instead operates on @rd values. Either use ++cook to automatically convert the input values from a 1.23-style input to the .~1.23 @rd style or build a different input parser from the entries in ++royl:so.
                              • Extend the calculator app so that it can support named variables (using @tas) with = tis. What new data structure do you need? For convenience, expose the result of the last operation as ans (a feature of TI graphing calculators and MATLAB, among other programs).
                              • The calculator app stack isn't really a proper CS stack with push and pop operations. Refactor it to use such a type.

                              Debugging Wrapper

                              ->

                              Edit this page on GitHub

                              Last modified June 13, 2023