diff --git a/docs/lexicon.html b/docs/lexicon.html index 7a2ecf710..ba838f200 100644 --- a/docs/lexicon.html +++ b/docs/lexicon.html @@ -411,6 +411,32 @@
ljust(width[,
+ fillchar])
+ - returns a copy of this string left-padded with the character given
+ in fillchar (or the default ' ' if no value is given) until it is width characters long.
+
+ rjust(width[,
+ fillchar])
+ - returns a copy of this string right-padded with the character given
+ in fillchar (or the default ' ' if no value is given) until it is width characters long.
+
+ bool:
pass
def format(self:str) -> str:
pass
+def ljust(self:str, width:int, fillchar:str=' ') -> str:
+ pass
+def rjust(self:str, width:int, fillchar:str=' ') -> str:
+ pass
def lstrip(self:str, cutset:str=' \n') -> str:
pass
def rstrip(self:str, cutset:str=' \n') -> str:
diff --git a/src/parse/asp/builtins.go b/src/parse/asp/builtins.go
index 353478b07..67f3d5cea 100644
--- a/src/parse/asp/builtins.go
+++ b/src/parse/asp/builtins.go
@@ -14,6 +14,7 @@ import (
"strings"
"sync"
"unicode"
+ "unicode/utf8"
"github.com/Masterminds/semver/v3"
"github.com/manifoldco/promptui"
@@ -88,6 +89,8 @@ func registerBuiltins(s *scope) {
"rpartition": setNativeCode(s, "rpartition", strRPartition),
"startswith": setNativeCode(s, "startswith", strStartsWith),
"endswith": setNativeCode(s, "endswith", strEndsWith),
+ "ljust": setNativeCode(s, "ljust", strLJust),
+ "rjust": setNativeCode(s, "rjust", strRJust),
"lstrip": setNativeCode(s, "lstrip", strLStrip),
"rstrip": setNativeCode(s, "rstrip", strRStrip),
"removeprefix": setNativeCode(s, "removeprefix", strRemovePrefix),
@@ -542,6 +545,35 @@ func strEndsWith(s *scope, args []pyObject) pyObject {
return newPyBool(strings.HasSuffix(string(self), string(x)))
}
+func strLJust(s *scope, args []pyObject) pyObject {
+ return strJust(s, args, true)
+}
+
+func strRJust(s *scope, args []pyObject) pyObject {
+ return strJust(s, args, false)
+}
+
+func strJust(s *scope, args []pyObject, left bool) pyObject {
+ self := string(args[0].(pyString))
+ width := int(args[1].(pyInt))
+ fillchar := string(args[2].(pyString))
+
+ // utf8.RuneCountInString rather than objLen, because objLen returns the length of a string in
+ // bytes rather than characters:
+ s.Assert(utf8.RuneCountInString(fillchar) == 1, "fillchar must be exactly one character long")
+
+ count := width - utf8.RuneCountInString(self)
+ // strings.Repeat's second operand must be non-negative, otherwise it panics. In that case, no
+ // changes need to be made to the input string anyway, so just return a copy of it.
+ if count <= 0 {
+ return pyString(self)
+ }
+ if left {
+ return pyString(strings.Repeat(fillchar, count) + self)
+ }
+ return pyString(self + strings.Repeat(fillchar, count))
+}
+
func strLStrip(s *scope, args []pyObject) pyObject {
self := args[0].(pyString)
cutset := args[1].(pyString)
diff --git a/src/parse/asp/interpreter_test.go b/src/parse/asp/interpreter_test.go
index 75c18f76c..aed64561c 100644
--- a/src/parse/asp/interpreter_test.go
+++ b/src/parse/asp/interpreter_test.go
@@ -730,3 +730,43 @@ func TestBreakLoop(t *testing.T) {
assert.NoError(t, err)
assert.EqualValues(t, pyString("ok"), s.Lookup("x"))
}
+
+func TestStrLjust(t *testing.T) {
+ s, err := parseFile("src/parse/asp/test_data/interpreter/str/ljust.build")
+ assert.NoError(t, err)
+ assert.EqualValues(t, pyString(" kittens"), s.Lookup("s1"))
+ assert.EqualValues(t, pyString("+++kittens"), s.Lookup("s2"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s3"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s4"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s5"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s6"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s7"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s8"))
+ assert.EqualValues(t, pyString("£££kittens"), s.Lookup("s9"))
+ assert.EqualValues(t, pyString("++++££kittens££"), s.Lookup("s10"))
+ // Make sure the original string wasn't modified:
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("orig"))
+
+ _, err = parseFile("src/parse/asp/test_data/interpreter/str/ljust_multiple_fillchars.build")
+ assert.Error(t, err, "fillchar must be exactly one character long")
+}
+
+func TestStrRjust(t *testing.T) {
+ s, err := parseFile("src/parse/asp/test_data/interpreter/str/rjust.build")
+ assert.NoError(t, err)
+ assert.EqualValues(t, pyString("kittens "), s.Lookup("s1"))
+ assert.EqualValues(t, pyString("kittens+++"), s.Lookup("s2"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s3"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s4"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s5"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s6"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s7"))
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("s8"))
+ assert.EqualValues(t, pyString("kittens£££"), s.Lookup("s9"))
+ assert.EqualValues(t, pyString("££kittens££++++"), s.Lookup("s10"))
+ // Make sure the original string wasn't modified:
+ assert.EqualValues(t, pyString("kittens"), s.Lookup("orig"))
+
+ _, err = parseFile("src/parse/asp/test_data/interpreter/str/rjust_multiple_fillchars.build")
+ assert.Error(t, err, "fillchar must be exactly one character long")
+}
diff --git a/src/parse/asp/test_data/interpreter/str/ljust.build b/src/parse/asp/test_data/interpreter/str/ljust.build
new file mode 100644
index 000000000..1c6e84b81
--- /dev/null
+++ b/src/parse/asp/test_data/interpreter/str/ljust.build
@@ -0,0 +1,16 @@
+orig = "kittens"
+
+s1 = orig.ljust(10)
+s2 = orig.ljust(10, "+")
+s3 = orig.ljust(len(orig))
+s4 = orig.ljust(len(orig), "+")
+s5 = orig.ljust(0)
+s6 = orig.ljust(0, "+")
+s7 = orig.ljust(-5)
+s8 = orig.ljust(-5, "+")
+
+# 1-character, 2-byte fillchar:
+s9 = orig.ljust(10, "£")
+
+# Input string containing a multi-byte character:
+s10 = "££kittens££".ljust(15, "+")
diff --git a/src/parse/asp/test_data/interpreter/str/ljust_multiple_fillchars.build b/src/parse/asp/test_data/interpreter/str/ljust_multiple_fillchars.build
new file mode 100644
index 000000000..982ae11ba
--- /dev/null
+++ b/src/parse/asp/test_data/interpreter/str/ljust_multiple_fillchars.build
@@ -0,0 +1 @@
+s = "kittens".ljust(10, "!!")
diff --git a/src/parse/asp/test_data/interpreter/str/rjust.build b/src/parse/asp/test_data/interpreter/str/rjust.build
new file mode 100644
index 000000000..871d25861
--- /dev/null
+++ b/src/parse/asp/test_data/interpreter/str/rjust.build
@@ -0,0 +1,16 @@
+orig = "kittens"
+
+s1 = orig.rjust(10)
+s2 = orig.rjust(10, "+")
+s3 = orig.rjust(len(orig))
+s4 = orig.rjust(len(orig), "+")
+s5 = orig.rjust(0)
+s6 = orig.rjust(0, "+")
+s7 = orig.rjust(-5)
+s8 = orig.rjust(-5, "+")
+
+# 1-character, 2-byte fillchar:
+s9 = orig.rjust(10, "£")
+
+# Input string containing a multi-byte character:
+s10 = "££kittens££".rjust(15, "+")
diff --git a/src/parse/asp/test_data/interpreter/str/rjust_multiple_fillchars.build b/src/parse/asp/test_data/interpreter/str/rjust_multiple_fillchars.build
new file mode 100644
index 000000000..678f6bb0b
--- /dev/null
+++ b/src/parse/asp/test_data/interpreter/str/rjust_multiple_fillchars.build
@@ -0,0 +1 @@
+s = "kittens".rjust(10, "!!")