diff --git a/PROGRESS.md b/CHANGELOG.md similarity index 70% rename from PROGRESS.md rename to CHANGELOG.md index 6918965..25b007b 100644 --- a/PROGRESS.md +++ b/CHANGELOG.md @@ -1,6 +1,69 @@ -# SE 3250 Progress Checkpoints +# SLS Changelog -## Checkpoint 3 +## 0.0.2-alpha +*08 Dec 2025* + +- Added Rust Port +- Added Interpreter State Serialization to Rust port + - Added `#load ` and `#save ` directives to the REPL +- Added Python Port +- Added Calculator app + - Added sls_py.calc module +- Added RP2040 build target for the C implementation + +## 0.0.1-alpha +*01 Dec 2025* + +- Added executing a file +- Implemented the following builtin operators: + - `for` + - `logb` + - `max` + - `min` + - `rot` + - `const` + - `atan2` + - `roll` + - `while` + - `type_of` + - `eval` + - `lambda` + - `if` + - `pick` + - `dup` + - bitwise `and` + - bitwise `not` + - bitwise `or` + - bitwise `xor` + - boolean `and` + - boolean `not` + - boolean `or` + - `shl` + - `shr` + - comparisons + - `ceil` + - `floor` + - `round` + - `swap` + - `seed` + - `rand` + - `acos` + - `asin` + - `atan` + - `cos` + - `ln` + - `log` + - `sin` + - `sqrt` + - `tan` + - `abs` + - modulus + - exponential + - addition + - subtraction + - multiplication + +## SE Checkpoint 3 *28 Nov 2025* [github.com/SnowSE/final-project-KylerOlsen](https://github.com/SnowSE/final-project-KylerOlsen) @@ -28,7 +91,7 @@ I'm not really stuck on anything, but I am running out of time, plus I am worried about not having enough AI usage to do the ports. If I at least get the main structures of the ports, I hope it will be enough for me to finish. -## Checkpoint 2 +## SE Checkpoint 2 *20 Nov 2025* [github.com/SnowSE/final-project-KylerOlsen](https://github.com/SnowSE/final-project-KylerOlsen) @@ -58,7 +121,7 @@ features moved to the backlog include: - Type tuples, function defs - Structs, unions, etc. -## Checkpoint 1 +## SE Checkpoint 1 *07 Nov 2025* **intended user**: diff --git a/README.md b/README.md index 4b89d06..b85dcd9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,128 @@ # YREA SLS *Kyler Olsen* -*October 2025* -*Snow College* -*SE 3250 Survey of Languages Final Project* +*October 2025* SLS is a statically-typed, stack-based language with pure postfix notation combining the execution model of HP's RPL, the type system of C and Rust, and modern array operations from Uiua. + +## Build Commands + +**Linux** + +C +```bash +cd SLS_C +python3 build.py build +./bin/sls +``` + +Python run module +```bash +cd SLS_Python +python3 -m sls_py +python3 -m sls_py.calc +``` + +Python Setup +```bash +cd SLS_Python +python -m venv .venv +source .venv/bin/activate +pip install build wheel "setuptools>=61.0" +pip install -e sls_build_backend +``` + +Python build module +```bash +cd SLS_Python +source .venv/bin/activate +python3 -m build --no-isolation +pip install ./dist/sls_python-0.0.2a0-py3-none-any.whl +python3 -m sls_py +python3 -m sls_py.calc +``` + +Rust +```bash +cd SLS_Rust/sls +cargo build +./target/debug/sls_rs +``` + +**Windows** + +C (Using Visual Studio Developer PowerShell) +```shell +cd SLS_C +python build.py build +.\bin\sls.exe +``` + +Python run module +```bat +cd SLS_Python +python -m sls_py +python -m sls_py.calc +``` + +Python Setup +```bat +cd SLS_Python +python -m venv .venv +.venv\Scripts\activate.bat +pip install build wheel "setuptools>=61.0" +pip install -e sls_build_backend +``` + +Python build module +```bat +cd SLS_Python +.venv\Scripts\activate.bat +python -m build --no-isolation +pip install .\dist\sls_python-0.0.2a0-py3-none-any.whl +python -m sls_py +python -m sls_py.calc +``` + +Rust +```bat +cd SLS_Rust\sls +cargo build +.\target\debug\sls_rs.exe +``` + +**MacOS** + +Reference Linux build instructions. + +For C there is the `python3 build.py macos` command, but it is untested as I +didn't test it on a Mac. I also don't know if `python3 build.py build` would also +work on a Mac. + +Python and Rust should just be the same or similar to Linux. + +**RP2040** + +Only tested on Linux. Only SLS_C supports RP2040. + +Install Pico SDK to `~/pico/pico-sdk`, or set environment variable +`PICO_SDK_PATH` to your installation path. You will also need to install the +appropriate compiler. + +Debian GNU/Linux +```bash +sudo apt install gcc-arm-none-eabi +``` + +```shell +cd SLS_C +python3 build.py rp2040 +``` + +Flash Raspberry Pi Pico: +Copy `.\build_pico\sls.elf.uf2` to your Pico in BOOTSEL mode + +## Contributing + +[sls.purplecello.org/contributing](https://sls.purplecello.org/contributing.html) diff --git a/REPORT.md b/REPORT.md index 096738f..4e5b8b9 100644 --- a/REPORT.md +++ b/REPORT.md @@ -2,7 +2,9 @@ *Kyler Olsen* *October-December 2025* *Snow College* -*SE 3250 Survey of Languages Final Project* +*SE 3250* +*Survey of Languages* +*Final Project* Language Code Name: YREA **SLS** (*Stack Language Specification*) @@ -17,8 +19,6 @@ Language Implementation Repository (Mirror on GitHub) (Private): Assignment Page (Private): [snow.instructure.com](https://snow.instructure.com/courses/1154808/assignments/16233203) -## Problem and Interest - In 1986, Hewlett-Packard released their HP-18C and HP-24C calculators, which introduced their new RPL operating system and programming language. The language was based on LISP and Forth (a stack-oriented language). RPL, aka Reverse Polish @@ -41,14 +41,25 @@ follows the pattern in RPL, everything is on the stack. One of my goals with this language is also to have it able to run on an embedded system, such as my own custom calculator. -## Languages - **C** was my selected language. It was an excellent choice for my project as it is well suited for systems programming. It is a low level, yet powerful language. While string utilities are not as robust as most modern languages, I still feel like it was an excellent choice. Other major interpreted languages also use C to implement their interpreters, such as Lua and Python. +Manual memory management was a slight difficulty for me. The only way I could +find what was causing a segmentation fault was to run the binary with `gdb`. +Still my experience programming C in a Linux environment was fun and rewarding. +I became accustomed to utilizing the man pages (or manual pages) for +documentation on the C standard library. + +I did use structs and unions heavily which we learned about when we looked at C. +With them I was able to use polymorphism in defining tokens and data types. + +I am able to successfully compile and run this implementation on a Raspberry Pi +Pico with a RP2040 microcontroller. You can interact with the REPL over a serial +connection. + **Rust** was the first language I tackled porting my project to. I am not as familiar with Rust as I am Python, so thats why I wanted to get going on this port first. I avoided using external libraries with C as they can be famously @@ -57,6 +68,10 @@ basically non-existent. It is also memory safe with its barrow checker. With it being another systems programming language, and these modern features, I feel like it is just as good of a choice of a language for my project. +In the past I have often fought with the barrow checker but with this project, +either my experience, memory mindfulness in my original port, or the extensive +AI help, I had no fights with the barrow checker this time. + The special feature for the Rust port is being able to export and import the interpreter state in the repl using `#save ` and `#load `. @@ -70,17 +85,8 @@ goal of portability to embedded systems. The special feature for the Python port is the SLS Calculator App. -## Lessons - -## Struggles - -In the past I have often fought with the barrow checker but with this project, -either my experience, memory mindfulness in my original port, or the extensive -AI help, I had no fights with the barrow checker this time. - --- https://en.wikipedia.org/wiki/HP_48_series https://en.wikipedia.org/wiki/HP-28_series https://en.wikipedia.org/wiki/RPL_(programming_language) -https://gitlab.com/da_doomer/markdown-slides diff --git a/SLS_C/fib.min.sls b/SLS_C/fib.min.sls new file mode 100644 index 0000000..058ffe0 --- /dev/null +++ b/SLS_C/fib.min.sls @@ -0,0 +1 @@ +{ 2 - dup 0 <= { drop 1 } { 1 1 rot 0 swap { drop swap 1 pick + } for swap drop } if } lambda ::fib const diff --git a/SLS_C/fib.sls b/SLS_C/fib.sls new file mode 100644 index 0000000..2b5dfba --- /dev/null +++ b/SLS_C/fib.sls @@ -0,0 +1,17 @@ +{ + // Start with which Fibonacci number we want (Nth Fibonacci number) + 2 - // We push the first two already + dup 0 <= { drop 1 } { + 1 1 // Starting Fibonacci numbers + rot 0 swap // Setting up loop from zero to N + { + drop // Discard the loop counter + swap 1 pick // Swap n-1 and n-2 and copy n-1 + + // Add n-2 and the copy of n-1 + // n and n-1 left on stack + } for + swap drop // Drop n-1 from the stack + } if +} lambda ::fib const + +8 fib diff --git a/SLS_C/include/sls/meta.h b/SLS_C/include/sls/meta.h index ad5ff9a..30a7445 100644 --- a/SLS_C/include/sls/meta.h +++ b/SLS_C/include/sls/meta.h @@ -7,7 +7,7 @@ #define SLS_MAIN_H #define SLS_NAME "SLS_C" -#define SLS_VER "0.0.1-alpha" +#define SLS_VER "0.0.2-alpha" #ifndef GIT_COMMIT_HASH #define GIT_COMMIT_HASH "UNKNOWN" diff --git a/SLS_C/tests/lexer_tests.c b/SLS_C/tests/lexer_tests.c index e817e21..5d41918 100644 --- a/SLS_C/tests/lexer_tests.c +++ b/SLS_C/tests/lexer_tests.c @@ -1803,8 +1803,8 @@ static TestResult test_Float_f32_Negative_Zero() { return pass_test(&test, result); } -static TestResult test_Char_Simple_Letter_A() { - LexerTest test = start_up_test(SLS_STR("Char Simple Letter A"), SLS_STR("'A'")); +static TestResult test_Char_Simple_Letter_Uppercase_A() { + LexerTest test = start_up_test(SLS_STR("Char Simple Letter Uppercase A"), SLS_STR("'A'")); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; @@ -1813,8 +1813,8 @@ static TestResult test_Char_Simple_Letter_A() { return pass_test(&test, result); } -static TestResult test_Char_Simple_Letter_a() { - LexerTest test = start_up_test(SLS_STR("Char Simple Letter a"), SLS_STR("'a'")); +static TestResult test_Char_Simple_Letter_Lowercase_a() { + LexerTest test = start_up_test(SLS_STR("Char Simple Letter Lowercase a"), SLS_STR("'a'")); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; @@ -1823,8 +1823,8 @@ static TestResult test_Char_Simple_Letter_a() { return pass_test(&test, result); } -static TestResult test_Char_Simple_Letter_Z() { - LexerTest test = start_up_test(SLS_STR("Char Simple Letter Z"), SLS_STR("'Z'")); +static TestResult test_Char_Simple_Letter_Uppercase_Z() { + LexerTest test = start_up_test(SLS_STR("Char Simple Letter Uppercase Z"), SLS_STR("'Z'")); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; @@ -1833,8 +1833,8 @@ static TestResult test_Char_Simple_Letter_Z() { return pass_test(&test, result); } -static TestResult test_Char_Simple_Letter_z() { - LexerTest test = start_up_test(SLS_STR("Char Simple Letter z"), SLS_STR("'z'")); +static TestResult test_Char_Simple_Letter_Lowercase_z() { + LexerTest test = start_up_test(SLS_STR("Char Simple Letter Lowercase z"), SLS_STR("'z'")); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; @@ -2073,22 +2073,22 @@ static TestResult test_Char_Right_Brace() { return pass_test(&test, result); } -static TestResult test_Char_Escape_Tab() { - LexerTest test = start_up_test(SLS_STR("Char Escape Tab"), SLS_STR("'\\t'")); +static TestResult test_Char_Escape_Single_quote() { + LexerTest test = start_up_test(SLS_STR("Char Escape Single quote"), SLS_STR("'\\''")); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; - if (test_character_value(&test, result, i++, &(uint8_t){9})) return test.result; + if (test_character_value(&test, result, i++, &(uint8_t){39})) return test.result; if (test_eof_value(&test, result, i++, 0)) return test.result; return pass_test(&test, result); } -static TestResult test_Char_Escape_Backslash() { - LexerTest test = start_up_test(SLS_STR("Char Escape Backslash"), SLS_STR("'\\\\'")); +static TestResult test_Char_Escape_Newline() { + LexerTest test = start_up_test(SLS_STR("Char Escape Newline"), SLS_STR("'\\n'")); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; - if (test_character_value(&test, result, i++, &(uint8_t){92})) return test.result; + if (test_character_value(&test, result, i++, &(uint8_t){10})) return test.result; if (test_eof_value(&test, result, i++, 0)) return test.result; return pass_test(&test, result); } @@ -2103,12 +2103,22 @@ static TestResult test_Char_Escape_Null_character() { return pass_test(&test, result); } -static TestResult test_Char_Escape_Single_quote() { - LexerTest test = start_up_test(SLS_STR("Char Escape Single quote"), SLS_STR("'\\''")); +static TestResult test_Char_Escape_Backslash() { + LexerTest test = start_up_test(SLS_STR("Char Escape Backslash"), SLS_STR("'\\\\'")); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; - if (test_character_value(&test, result, i++, &(uint8_t){39})) return test.result; + if (test_character_value(&test, result, i++, &(uint8_t){92})) return test.result; + if (test_eof_value(&test, result, i++, 0)) return test.result; + return pass_test(&test, result); +} + +static TestResult test_Char_Escape_Tab() { + LexerTest test = start_up_test(SLS_STR("Char Escape Tab"), SLS_STR("'\\t'")); + LexerResult result = lexical_analysis(&test.lexer_info); + if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); + size_t i = 0; + if (test_character_value(&test, result, i++, &(uint8_t){9})) return test.result; if (test_eof_value(&test, result, i++, 0)) return test.result; return pass_test(&test, result); } @@ -2123,16 +2133,6 @@ static TestResult test_Char_Escape_Carriage_return() { return pass_test(&test, result); } -static TestResult test_Char_Escape_Newline() { - LexerTest test = start_up_test(SLS_STR("Char Escape Newline"), SLS_STR("'\\n'")); - LexerResult result = lexical_analysis(&test.lexer_info); - if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); - size_t i = 0; - if (test_character_value(&test, result, i++, &(uint8_t){10})) return test.result; - if (test_eof_value(&test, result, i++, 0)) return test.result; - return pass_test(&test, result); -} - static TestResult test_Char_With_Leading_Whitespace() { LexerTest test = start_up_test(SLS_STR("Char With Leading Whitespace"), SLS_STR(" 'A'")); LexerResult result = lexical_analysis(&test.lexer_info); @@ -3845,10 +3845,10 @@ TestsReport run_lexer_tests() { test_report.tests[i++] = test_Float_Default_Negative_Zero(); test_report.tests[i++] = test_Float_f32_Positive_Zero(); test_report.tests[i++] = test_Float_f32_Negative_Zero(); - test_report.tests[i++] = test_Char_Simple_Letter_A(); - test_report.tests[i++] = test_Char_Simple_Letter_a(); - test_report.tests[i++] = test_Char_Simple_Letter_Z(); - test_report.tests[i++] = test_Char_Simple_Letter_z(); + test_report.tests[i++] = test_Char_Simple_Letter_Uppercase_A(); + test_report.tests[i++] = test_Char_Simple_Letter_Lowercase_a(); + test_report.tests[i++] = test_Char_Simple_Letter_Uppercase_Z(); + test_report.tests[i++] = test_Char_Simple_Letter_Lowercase_z(); test_report.tests[i++] = test_Char_Digit_0(); test_report.tests[i++] = test_Char_Digit_5(); test_report.tests[i++] = test_Char_Digit_9(); @@ -3872,12 +3872,12 @@ TestsReport run_lexer_tests() { test_report.tests[i++] = test_Char_Right_Bracket(); test_report.tests[i++] = test_Char_Left_Brace(); test_report.tests[i++] = test_Char_Right_Brace(); - test_report.tests[i++] = test_Char_Escape_Tab(); - test_report.tests[i++] = test_Char_Escape_Backslash(); - test_report.tests[i++] = test_Char_Escape_Null_character(); test_report.tests[i++] = test_Char_Escape_Single_quote(); - test_report.tests[i++] = test_Char_Escape_Carriage_return(); test_report.tests[i++] = test_Char_Escape_Newline(); + test_report.tests[i++] = test_Char_Escape_Null_character(); + test_report.tests[i++] = test_Char_Escape_Backslash(); + test_report.tests[i++] = test_Char_Escape_Tab(); + test_report.tests[i++] = test_Char_Escape_Carriage_return(); test_report.tests[i++] = test_Char_With_Leading_Whitespace(); test_report.tests[i++] = test_Char_With_Trailing_Whitespace(); test_report.tests[i++] = test_Char_With_Both_Whitespace(); diff --git a/SLS_Python/pyproject.toml b/SLS_Python/pyproject.toml index f909e38..10ab98a 100644 --- a/SLS_Python/pyproject.toml +++ b/SLS_Python/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "sls_build_backend" [project] name = "sls_python" -version = "0.0.1-alpha" +version = "0.0.2-alpha" description = "Python reimplementation of the SLS C project" authors = [ { name = "Kyler Olsen" } ] readme = "README.md" diff --git a/SLS_Python/sls_calc/__init__.py b/SLS_Python/sls_calc/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/SLS_Python/sls_py/_version.py b/SLS_Python/sls_py/_version.py index 0deac39..fc159ef 100644 --- a/SLS_Python/sls_py/_version.py +++ b/SLS_Python/sls_py/_version.py @@ -1,6 +1,6 @@ import subprocess from datetime import datetime, timezone -version = "0.0.1-alpha" +version = "0.0.2-alpha" try: __result_hash = subprocess.check_output( ["git", "describe", "--always", "--dirty", "--abbrev=7"], diff --git a/SLS_Python/sls_calc/__main__.py b/SLS_Python/sls_py/calc/__main__.py similarity index 95% rename from SLS_Python/sls_calc/__main__.py rename to SLS_Python/sls_py/calc/__main__.py index d1a29b7..819c4db 100644 --- a/SLS_Python/sls_calc/__main__.py +++ b/SLS_Python/sls_py/calc/__main__.py @@ -6,7 +6,13 @@ Implements classic HP calculator interface with stack display import tkinter as tk from tkinter import ttk, font as tkfont -import sls_py +from .. import ( + InterpreterState, + LexerInfo, + lexical_analysis, + TokenType, + StackType, +) class SlsCalculator: def __init__(self, root): @@ -16,8 +22,8 @@ class SlsCalculator: self.root.resizable(False, False) # Initialize interpreter - self.interp = sls_py.InterpreterState() - self.lexer = sls_py.LexerInfo() + self.interp = InterpreterState() + self.lexer = LexerInfo() # Current entry buffer self.entry_buffer = "" @@ -199,10 +205,10 @@ class SlsCalculator: self.lexer.column = 1 self.lexer.line = 1 - tokens = sls_py.lexical_analysis(self.lexer) + tokens = lexical_analysis(self.lexer) for token in tokens: - if token.type == sls_py.TokenType.EOF: + if token.type == TokenType.EOF: break if not self.interp.execute(token): print(f"Error executing: {code}") @@ -235,9 +241,9 @@ class SlsCalculator: def format_stack_entry(self, entry): """Format a stack entry for display""" - if entry.type == sls_py.StackType.I64: + if entry.type == StackType.I64: return str(entry.value) - elif entry.type == sls_py.StackType.DOUBLE: + elif entry.type == StackType.DOUBLE: val = entry.value # Format with appropriate precision if abs(val) < 1e-10 and val != 0: @@ -246,7 +252,7 @@ class SlsCalculator: return f"{val:.6e}" else: return f"{val:.10g}" - elif entry.type == sls_py.StackType.BOOLEAN: + elif entry.type == StackType.BOOLEAN: return str(entry.value) else: return str(entry.value) diff --git a/SLS_Python/tests/test_import.py b/SLS_Python/tests/test_import.py deleted file mode 100644 index add2468..0000000 --- a/SLS_Python/tests/test_import.py +++ /dev/null @@ -1,5 +0,0 @@ -def test_import_package(): - import sls - - assert hasattr(sls, "__version__") - assert isinstance(sls.__version__, str) diff --git a/SLS_Rust/sls/Cargo.toml b/SLS_Rust/sls/Cargo.toml index 47d7338..c8ef753 100644 --- a/SLS_Rust/sls/Cargo.toml +++ b/SLS_Rust/sls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sls_rs" -version = "0.0.1-alpha" +version = "0.0.2-alpha" edition = "2021" [dependencies] diff --git a/SLS_Rust/sls/src/main.rs b/SLS_Rust/sls/src/main.rs index b86be00..96b10fa 100644 --- a/SLS_Rust/sls/src/main.rs +++ b/SLS_Rust/sls/src/main.rs @@ -12,7 +12,7 @@ use repl::repl; // These mirror the C macros. const SLS_NAME: &str = "SLS_RUST"; -const SLS_VER: &str = "0.0.1-alpha"; +const SLS_VER: &str = "0.0.2-alpha"; pub fn print_version() { let git_hash = option_env!("GIT_COMMIT_HASH").unwrap_or("unknown");