From c0c35e4d17cdfd805696e386bed9279620635955 Mon Sep 17 00:00:00 2001 From: SpookyDervish Date: Mon, 13 Apr 2026 19:59:48 +1000 Subject: [PATCH] started writing lexer after making string lib --- src/lexer/lexer.sols | 95 ++++++++++++++++++++++++-- string/SUMMARY.md | 37 ++++++++++ string/docs/char_at.md | 24 +++++++ string/docs/check/contains.md | 25 +++++++ string/docs/check/ends_with.md | 25 +++++++ string/docs/check/starts_with.md | 25 +++++++ string/docs/classify/is_alnum.md | 24 +++++++ string/docs/classify/is_alpha.md | 24 +++++++ string/docs/classify/is_digit copy.md | 24 +++++++ string/docs/classify/is_space.md | 24 +++++++ string/docs/find/count.md | 21 ++++++ string/docs/find/find.md | 21 ++++++ string/docs/find/find_last.md | 21 ++++++ string/docs/modify/lower.md | 23 +++++++ string/docs/modify/repeat.md | 24 +++++++ string/docs/modify/replace.md | 22 ++++++ string/docs/modify/reverse.md | 23 +++++++ string/docs/modify/substring.md | 27 ++++++++ string/docs/modify/trim.md | 23 +++++++ string/docs/modify/trim_left.md | 23 +++++++ string/docs/modify/trim_right.md | 23 +++++++ string/docs/modify/upper.md | 23 +++++++ string/main.so | Bin 0 -> 21264 bytes string/mineral.ini | 7 ++ 24 files changed, 601 insertions(+), 7 deletions(-) create mode 100644 string/SUMMARY.md create mode 100644 string/docs/char_at.md create mode 100644 string/docs/check/contains.md create mode 100644 string/docs/check/ends_with.md create mode 100644 string/docs/check/starts_with.md create mode 100644 string/docs/classify/is_alnum.md create mode 100644 string/docs/classify/is_alpha.md create mode 100644 string/docs/classify/is_digit copy.md create mode 100644 string/docs/classify/is_space.md create mode 100644 string/docs/find/count.md create mode 100644 string/docs/find/find.md create mode 100644 string/docs/find/find_last.md create mode 100644 string/docs/modify/lower.md create mode 100644 string/docs/modify/repeat.md create mode 100644 string/docs/modify/replace.md create mode 100644 string/docs/modify/reverse.md create mode 100644 string/docs/modify/substring.md create mode 100644 string/docs/modify/trim.md create mode 100644 string/docs/modify/trim_left.md create mode 100644 string/docs/modify/trim_right.md create mode 100644 string/docs/modify/upper.md create mode 100755 string/main.so create mode 100644 string/mineral.ini diff --git a/src/lexer/lexer.sols b/src/lexer/lexer.sols index ce077ba..d6f64b0 100644 --- a/src/lexer/lexer.sols +++ b/src/lexer/lexer.sols @@ -1,16 +1,97 @@ use collections +use string + +// +// Token type enum. +// +// Each token can be one of these types. +// +enum SolsToken { + Identifier, Literal, Type, + + Dot, OpenCurly, CloseCurly, OpenParen, CloseParen, Comma, + + OpAdd, OpSub, OpMul, OpDiv, OpAddTo, OpSubTo, OpMulTo, OpDivTo, OpIncrement, OpDecrement, OpSet, + OpGreater, OpLesser, OpEqual, OpInequal, OpEqGreater, OpEqLesser, + + KwDef, KwLambda, KwReturn, KwUse, KwStruct, KwEnum, KwConstructor, + KwDestructor, KwDuplicator, KwPrivate, KwProtected, KwPuts, KwIf, + KwWhile, KwNew, KwGround, + + LineEnd, + +} + +// +// Token type map. +// +// If a token matches one of the strings in this hashmap, +// its type gets set to the token type for that string. +// +KEYWORDS = Hashmap() +KEYWORDS.set("puts", SolsToken.KwPuts) +KEYWORDS.set("if", SolsToken.KwIf) +KEYWORDS.set("while", SolsToken.KwWhile) +KEYWORDS.set("def", SolsToken.KwDef) +KEYWORDS.set("lambda", SolsToken.KwLambda) +KEYWORDS.set("return", SolsToken.KwReturn) +KEYWORDS.set("use", SolsToken.KwUse) +KEYWORDS.set("struct", SolsToken.KwStruct) +KEYWORDS.set("enum", SolsToken.KwEnum) +KEYWORDS.set("constructor", SolsToken.KwConstructor) +KEYWORDS.set("destructor", SolsToken.KwDestructor) +KEYWORDS.set("duplicator", SolsToken.KwDuplicator) +KEYWORDS.set("private", SolsToken.KwPrivate) +KEYWORDS.set("protected", SolsToken.KwProtected) +KEYWORDS.set("ground", SolsToken.KwGround) +KEYWORDS.set("new", SolsToken.KwNew) +KEYWORDS.set("{", SolsToken.OpenCurly) +KEYWORDS.set("}", SolsToken.CloseCurly) +KEYWORDS.set("(", SolsToken.OpenParen) +KEYWORDS.set(")", SolsToken.CloseParen) +KEYWORDS.set("+", SolsToken.OpAdd) +KEYWORDS.set("-", SolsToken.OpSub) +KEYWORDS.set("*", SolsToken.OpMul) +KEYWORDS.set("/", SolsToken.OpDiv) +KEYWORDS.set("=", SolsToken.OpSet) +KEYWORDS.set("+=", SolsToken.OpAddTo) +KEYWORDS.set("-=", SolsToken.OpSubTo) +KEYWORDS.set("*=", SolsToken.OpMulTo) +KEYWORDS.set("/=", SolsToken.OpDivTo) +KEYWORDS.set("++", SolsToken.OpIncrement) +KEYWORDS.set("--", SolsToken.OpDecrement) +KEYWORDS.set("==", SolsToken.OpEqual) +KEYWORDS.set("!=", SolsToken.OpInequal) +KEYWORDS.set(">", SolsToken.OpGreater) +KEYWORDS.set("<", SolsToken.OpLesser) +KEYWORDS.set(">=", SolsToken.OpEqGreater) +KEYWORDS.set("<=", SolsToken.OpEqLesser) +KEYWORDS.set("\n", SolsToken.LineEnd) +KEYWORDS.set(";", SolsToken.LineEnd) +KEYWORDS.set(",", SolsToken.Comma) struct SolsLexer { - input = "" + sourceCode = "x = 123" current = 0 - constructor(string input) { - self.input = input + private def getTokenType(string input) SolsToken { + return KEYWORDS.get(input) } - def lex() List { - output = List(1) + def lex() { + inString = false - return output + lineNum = 1 + lineStart = 0 + currentLine = "" + + while lineStart < string_Length() { + + } } -} \ No newline at end of file +} + + + +lexer = new SolsLexer +puts lexer.getTokenType("if") \ No newline at end of file diff --git a/string/SUMMARY.md b/string/SUMMARY.md new file mode 100644 index 0000000..5b29ed4 --- /dev/null +++ b/string/SUMMARY.md @@ -0,0 +1,37 @@ +# string +The string library gives you access to a lot of functions that are necessary for doing anything useful with strings. +**Note:** The string in Ground library is called "strings" in Solstice. + +## Functions +- [string_CharAt](docs/char_at.md) + +### Modifying Strings +- [string_Upper](docs/modify/upper.md) +- [string_Lower](docs/modify/lower.md) +- [string_Trim](docs/modify/trim.md) +- [string_TrimLeft](docs/modify/trim_left.md) +- [string_TrimRight](docs/modify/trim_right.md) +- [string_Substring](docs/modify/substring.md) +- [string_Repeat](docs/modify/repeat.md) +- [string_Replace](docs/modify/replace.md) +- [string_Reverse](docs/modify/reverse.md) + +### Check For Substring +- [string_StartsWith](docs/check/starts_with.md) +- [string_EndsWith](docs/check/ends_with.md) +- [string_Contains](docs/check/contains.md) + +### Find Substring +- [string_Find](docs/find/find.md) +- [string_FindLast](docs/find/find_last.md) +- [string_Count](docs/find/count.md) + +### String Classification +- [string_IsAlpha](docs/classify/is_alpha.md) +- [string_IsAlnum](docs/classify/is_alnum.md) +- [string_IsDigit](docs/classify/is_digit.md) +- [string_IsSpace](docs/classify/is_space.md) + +## Changelog +### v1.0.0 (latest) +- Initial release. diff --git a/string/docs/char_at.md b/string/docs/char_at.md new file mode 100644 index 0000000..832bf11 --- /dev/null +++ b/string/docs/char_at.md @@ -0,0 +1,24 @@ +# string_CharAt +Get character at index in string. + +## Arguments +- str (string): string to get character of. +- index (int): the index in the string you want to get. + +## Returns +char (string): the character at the specified index in the string. + +## Raises +- `OutOfBounds`: raised if you try accessing a string past its length or at a negative index. + +## Example +### Ground +```python +call !string_CharAt "my string" 3 &char +println $char # "s" +``` + +### Solstice +```c +puts string_CharAt("my string", 3) // "s" +``` \ No newline at end of file diff --git a/string/docs/check/contains.md b/string/docs/check/contains.md new file mode 100644 index 0000000..6ac3178 --- /dev/null +++ b/string/docs/check/contains.md @@ -0,0 +1,25 @@ +# string_Contains +Check if a string has another string in it. + +## Arguments +- haystack (string): the string to check against. +- needle (string): the string to look for. + +## Returns +found (bool): returns `true` if `haystack` has `needle` inside it. + +## Example +### Ground +```python +call !string_Contains "The task was completed successfully." "success" &found +println $found # true + +call !string_Contains "The task failed." "success" &found +println $found # false +``` + +### Solstice +```c +puts string_Contains("The task was completed successfully.", "success") // true +puts string_Contains("The task failed.", "success") // false +``` \ No newline at end of file diff --git a/string/docs/check/ends_with.md b/string/docs/check/ends_with.md new file mode 100644 index 0000000..3c9fe2a --- /dev/null +++ b/string/docs/check/ends_with.md @@ -0,0 +1,25 @@ +# string_EndsWith +Check if a string has another string as a suffix. + +## Arguments +- str (string): the string to check against. +- suffix (string): the suffix to check for. + +## Returns +hasSuffix (bool): returns `true` if `str` has the given `suffix` as a suffix, otherwise `false`. + +## Example +### Ground +```python +call !string_EndsWith "file.txt" ".txt" &hasSuffix +println $hasSuffix # true + +call !string_EndsWith "file.grnd" ".txt" &hasSuffix +println $hasSuffix # false +``` + +### Solstice +```c +puts string_EndsWith("file.txt", ".txt") // true +puts string_EndsWith("file.grnd", ".txt") // false +``` \ No newline at end of file diff --git a/string/docs/check/starts_with.md b/string/docs/check/starts_with.md new file mode 100644 index 0000000..acd8095 --- /dev/null +++ b/string/docs/check/starts_with.md @@ -0,0 +1,25 @@ +# string_StartsWith +Check if a string is prefixed by another string. + +## Arguments +- str (string): the string to check against. +- prefix (string): the prefix to check for. + +## Returns +hasPrefix (bool): returns `true` if `str` is prefixed by the given `prefix`, otherwise `false`. + +## Example +### Ground +```python +call !string_StartsWith "doc_someFile.txt" "doc_" &hasPrefix +println $hasPrefix # true + +call !string_StartsWith "someFile.txt" "doc_" &hasPrefix +println $hasPrefix # false +``` + +### Solstice +```c +puts string_StartsWith("doc_someFile.txt", "doc_") // true +puts string_StartsWith("someFile.txt", "doc_") // false +``` \ No newline at end of file diff --git a/string/docs/classify/is_alnum.md b/string/docs/classify/is_alnum.md new file mode 100644 index 0000000..5e2fab4 --- /dev/null +++ b/string/docs/classify/is_alnum.md @@ -0,0 +1,24 @@ +# string_IsAlnum +Returns true if the given `string` is alphanumeric. + +## Arguments +- str (string): the string to check. + +## Returns +isAlnum (bool): returns `true` if `string` is alphanumeric, otherwise `false`. + +## Example +### Ground +```python +call !string_IsAlnum "abc123" &isAlnum +println $isAlnum # true + +call !string_IsAlnum "@$!ffasdf" &isAlnum +println $isAlnum # false + +call !string_IsAlnum "1234" &isAlnum +println $isAlnum # true + +call !string_IsAlnum "_" &isAlnum +println $isAlnum # false +``` \ No newline at end of file diff --git a/string/docs/classify/is_alpha.md b/string/docs/classify/is_alpha.md new file mode 100644 index 0000000..02577e2 --- /dev/null +++ b/string/docs/classify/is_alpha.md @@ -0,0 +1,24 @@ +# string_IsAlpha +Returns true if the given `string` is only the letters a-Z. + +## Arguments +- str (string): the string to check. + +## Returns +isAlpha (bool): returns `true` if `string` is only the letters a-Z, otherwise `false`. + +## Example +### Ground +```python +call !string_IsAlpha "abcABC" &isAlpha +println $isAlpha # true + +call !string_IsAlpha "123" &isAlpha +println $isAlpha # false + +call !string_IsAlpha "abc_$@" &isAlpha +println $isAlpha # false + +call !string_IsAlpha "aAbBcCdDeEfF" &isAlpha +println $isAlpha # true +``` \ No newline at end of file diff --git a/string/docs/classify/is_digit copy.md b/string/docs/classify/is_digit copy.md new file mode 100644 index 0000000..e6b8ccf --- /dev/null +++ b/string/docs/classify/is_digit copy.md @@ -0,0 +1,24 @@ +# string_IsAlpha +Returns true if the given `string` is only numbers. + +## Arguments +- str (string): the string to check. + +## Returns +isAlpha (bool): returns `true` if `string` is only numbers, otherwise `false`. + +## Example +### Ground +```python +call !string_IsAlpha "65535" &isAlpha +println $isAlpha # true + +call !string_IsAlpha "$!@/_ffff" &isAlpha +println $isAlpha # false + +call !string_IsAlpha "1234" &isAlpha +println $isAlpha # true + +call !string_IsAlpha "abcd123" &isAlpha +println $isAlpha # false +``` \ No newline at end of file diff --git a/string/docs/classify/is_space.md b/string/docs/classify/is_space.md new file mode 100644 index 0000000..6ee730f --- /dev/null +++ b/string/docs/classify/is_space.md @@ -0,0 +1,24 @@ +# string_IsSpace +Returns true if the given `string` is whitespace. + +## Arguments +- str (string): the string to check. + +## Returns +isAlpha (bool): returns `true` if `string` is whitespace, otherwise `false`. + +## Example +### Ground +```python +call !string_IsAlpha " " &isAlpha +println $isAlpha # true + +call !string_IsAlpha " " &isAlpha +println $isAlpha # true + +call !string_IsAlpha "abc_def" &isAlpha +println $isAlpha # false + +call !string_IsAlpha " a" &isAlpha +println $isAlpha # false +``` \ No newline at end of file diff --git a/string/docs/find/count.md b/string/docs/find/count.md new file mode 100644 index 0000000..02ba699 --- /dev/null +++ b/string/docs/find/count.md @@ -0,0 +1,21 @@ +# string_Count +Count the number of times `needle` appears in `haystack`. + +## Arguments +- haystack (string): the string to search through. +- needle (string): the substring to count. + +## Returns +count (int): the number of times `needle` appears in `haystack`. + +## Example +### Ground +```python +call !string_Count "Hello, World!" "l" &count +println $count # 3 +``` + +### Solstice +```c +puts string_Count("Hello, World!", "l") // 3 +``` \ No newline at end of file diff --git a/string/docs/find/find.md b/string/docs/find/find.md new file mode 100644 index 0000000..99ed30b --- /dev/null +++ b/string/docs/find/find.md @@ -0,0 +1,21 @@ +# string_Find +Look for the first occurence of `needle` in `haystack` and return its index in the string. If it isn't found, return -1. + +## Arguments +- haystack (string): the string to search. +- needle (string): the substring to look for. + +## Returns +index (int): the index of the first occurence of `needle` in `haystack`. If it isn't found, return -1. + +## Example +### Ground +```python +call !string_Find "abcdefg" "d" &index +println $index # 3 +``` + +### Solstice +```c +puts string_Find("abcdefg", "d") // 3 +``` \ No newline at end of file diff --git a/string/docs/find/find_last.md b/string/docs/find/find_last.md new file mode 100644 index 0000000..35f772d --- /dev/null +++ b/string/docs/find/find_last.md @@ -0,0 +1,21 @@ +# string_FindLast +Look for the last occurence of `needle` in `haystack` and return its index in the string. If it isn't found, return -1. + +## Arguments +- haystack (string): the string to search. +- needle (string): the substring to look for. + +## Returns +index (int): the index of the last occurence of `needle` in `haystack`. If it isn't found, return -1. + +## Example +### Ground +```python +call !string_FindLast "abcabcabc" "b" &index +println $index # 7 +``` + +### Solstice +```c +puts string_FindLast("abcabcabc", "b") // 7 +``` \ No newline at end of file diff --git a/string/docs/modify/lower.md b/string/docs/modify/lower.md new file mode 100644 index 0000000..c501c82 --- /dev/null +++ b/string/docs/modify/lower.md @@ -0,0 +1,23 @@ +# string_Lower +Make a string lowercase. + +## Arguments +- str (string): the string to make lowercase. + +## Returns +lowerString (string): the string in lowercase. + +## Raises +- `AllocFail`: raised if Ground failed to allocate memory for the new string. + +## Example +### Ground +```python +call !string_Lower "my STRING" &lowerString +println $lowerString # "my string" +``` + +### Solstice +```c +puts string_Lower("my STRING") // "my string" +``` \ No newline at end of file diff --git a/string/docs/modify/repeat.md b/string/docs/modify/repeat.md new file mode 100644 index 0000000..091d351 --- /dev/null +++ b/string/docs/modify/repeat.md @@ -0,0 +1,24 @@ +# string_Repeat +Repeat a string `times` times. + +## Arguments +- str (string): the string to repeat. +- times (int): the number of times to repeat the string. + +## Returns +result (string): the string repeated the given number of times. + +## Raises +- `AllocFail`: raised if Ground fails to allocate memory for the new string. + +## Example +### Ground +```python +call !string_Repeat "hi " 5 &repeated +println $repeated # "hi hi hi hi hi " +``` + +### Solstice +```c +puts string_Repeat("hi ", 5) // "hi hi hi hi hi " +``` \ No newline at end of file diff --git a/string/docs/modify/replace.md b/string/docs/modify/replace.md new file mode 100644 index 0000000..c44b73a --- /dev/null +++ b/string/docs/modify/replace.md @@ -0,0 +1,22 @@ +# string_Replace +Replace all occurences of `from` in `str` with `to`. + +## Arguments +- str (string): the string to run a find and replace on. +- from (string): the substring you want to replace. +- to (string): the thing to replace all occurences of `from` with. + +## Returns +result (string): the resulting string after the find and replace. + +## Example +### Ground +```python +call !string_Replace "abacabacacacabacacacabac" "c" "b" &result +println $result # "abababababababababababab" +``` + +### Solstice +```c +puts string_Replace("abacabacacacabacacacabac", "c", "b") // "abababababababababababab" +``` \ No newline at end of file diff --git a/string/docs/modify/reverse.md b/string/docs/modify/reverse.md new file mode 100644 index 0000000..2ac921c --- /dev/null +++ b/string/docs/modify/reverse.md @@ -0,0 +1,23 @@ +# string_Reverse +Get the reversed version of a string. + +## Arguments +- str (string): the string to reverse. + +## Returns +result (string): the reversed string. + +## Raises +- `AllocFail`: raised if Ground failed to allocate memory for the new string. + +## Example +### Ground +```python +call !string_Reverse "Hello, World!" &reverse +println $reverse # "!dlroW, olleH" +``` + +### Solstice +```c +puts string_Reverse("Hello, World!") // "!dlroW, olleH" +``` \ No newline at end of file diff --git a/string/docs/modify/substring.md b/string/docs/modify/substring.md new file mode 100644 index 0000000..e46940d --- /dev/null +++ b/string/docs/modify/substring.md @@ -0,0 +1,27 @@ +# string_Substring +Get a substring of a string. + +## Arguments +- str (string): the string to get a substring of. +- start (int): starting index of the substring. +- end (int): ending index of the substring. + +## Returns +substring (string): the substring of the given string. + +## Raises +- `AllocFail`: raised if Ground failed to allocate memory for the new string. +- `EndBeforeStart`: raised if the `end` index is less than the `start` index. +- `OutOfBounds`: raised if either the `end` or `start` index is outside the bounds of the string. + +## Example +### Ground +```python +call !string_Substring "Hello, World!" 1 3 &sub +println $sub # "ell" +``` + +### Solstice +```c +puts string_Substring("Hello, World!", 1, 3) // "ell" +``` \ No newline at end of file diff --git a/string/docs/modify/trim.md b/string/docs/modify/trim.md new file mode 100644 index 0000000..88e822b --- /dev/null +++ b/string/docs/modify/trim.md @@ -0,0 +1,23 @@ +# string_Trim +Trim whitespace from both sides of a string. + +## Arguments +- str (string): the string to trim. + +## Returns +trimmed (string): the string with its whitespace removed. + +## Raises +- `AllocFail`: raised if Ground failed to allocate memory for the new string. + +## Example +### Ground +```python +call !string_Trim " aaabbb " &trimmed +println $trimmed # "aaabbb" +``` + +### Solstice +```c +puts string_Trim(" aaabbb ") // "aaabbb" +``` \ No newline at end of file diff --git a/string/docs/modify/trim_left.md b/string/docs/modify/trim_left.md new file mode 100644 index 0000000..add1055 --- /dev/null +++ b/string/docs/modify/trim_left.md @@ -0,0 +1,23 @@ +# string_TrimLeft +Trim whitespace from the left side of a string. + +## Arguments +- str (string): the string to trim. + +## Returns +trimmed (string): the string with the whitespace removed on the left side. + +## Raises +- `AllocFail`: raised if Ground failed to allocate memory for the new string. + +## Example +### Ground +```python +call !string_TrimLeft " aaabbb " &trimmed +println $trimmed # "aaabbb " +``` + +### Solstice +```c +puts string_TrimLeft(" aaabbb ") // "aaabbb " +``` \ No newline at end of file diff --git a/string/docs/modify/trim_right.md b/string/docs/modify/trim_right.md new file mode 100644 index 0000000..f884b03 --- /dev/null +++ b/string/docs/modify/trim_right.md @@ -0,0 +1,23 @@ +# string_TrimRight +Trim whitespace from the right side of a string. + +## Arguments +- str (string): the string to trim. + +## Returns +trimmed (string): the string with the whitespace removed on the right side. + +## Raises +- `AllocFail`: raised if Ground failed to allocate memory for the new string. + +## Example +### Ground +```python +call !string_TrimRight " aaabbb " &trimmed +println $trimmed # " aaabbb" +``` + +### Solstice +```c +puts string_TrimRight(" aaabbb ") // " aaabbb" +``` \ No newline at end of file diff --git a/string/docs/modify/upper.md b/string/docs/modify/upper.md new file mode 100644 index 0000000..5e7a5d1 --- /dev/null +++ b/string/docs/modify/upper.md @@ -0,0 +1,23 @@ +# string_Upper +Make a string uppercase. + +## Arguments +- str (string): the string to make uppercase. + +## Returns +upperString (string): the string in uppercase. + +## Raises +- `AllocFail`: raised if Ground failed to allocate memory for the new string. + +## Example +### Ground +```python +call !string_Upper "MY string" &upperString +println $upperString # "MY STRING" +``` + +### Solstice +```c +puts string_Upper("MY string") // "MY STRING" +``` \ No newline at end of file diff --git a/string/main.so b/string/main.so new file mode 100755 index 0000000000000000000000000000000000000000..91dcf73f67da3d8377ca9fe43eb1d3c64b8ad5e0 GIT binary patch literal 21264 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