🐐 String uppercase/lowercase functions
This commit is contained in:
10
README.md
10
README.md
@@ -391,6 +391,16 @@ Replaces all non-overlapping matches of the regex `pattern` in `string` with `re
|
|||||||
|
|
||||||
Splits `string` by `delimiter` and returns an array of substrings. Both arguments must be strings. If the delimiter is not found, returns a single-element array containing the original string. If the delimiter is an empty string, splits into individual characters.
|
Splits `string` by `delimiter` and returns an array of substrings. Both arguments must be strings. If the delimiter is not found, returns a single-element array containing the original string. If the delimiter is an empty string, splits into individual characters.
|
||||||
|
|
||||||
|
### MAIVSCVLA
|
||||||
|
`MAIVSCVLA(string)`
|
||||||
|
|
||||||
|
Returns a new string with every ASCII letter `a`–`z` replaced by its uppercase counterpart `A`–`Z`. All other bytes (digits, punctuation, non-ASCII) pass through unchanged.
|
||||||
|
|
||||||
|
### MINVSCVLA
|
||||||
|
`MINVSCVLA(string)`
|
||||||
|
|
||||||
|
Returns a new string with every ASCII letter `A`–`Z` replaced by its lowercase counterpart `a`–`z`. All other bytes (digits, punctuation, non-ASCII) pass through unchanged.
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
Modules are additions to the base `CENTVRION` syntax. They add or change certain features. Modules are included in your code by having
|
Modules are additions to the base `CENTVRION` syntax. They add or change certain features. Modules are included in your code by having
|
||||||
|
|
||||||
|
|||||||
@@ -1445,6 +1445,22 @@ class BuiltIn(Node):
|
|||||||
if len(params) != 1:
|
if len(params) != 1:
|
||||||
raise CentvrionError("LITTERA takes exactly I argument")
|
raise CentvrionError("LITTERA takes exactly I argument")
|
||||||
return vtable, ValStr(make_string(params[0], magnvm, svbnvlla))
|
return vtable, ValStr(make_string(params[0], magnvm, svbnvlla))
|
||||||
|
case "MAIVSCVLA":
|
||||||
|
if len(params) != 1:
|
||||||
|
raise CentvrionError("MAIVSCVLA takes exactly I argument")
|
||||||
|
val = params[0]
|
||||||
|
if not isinstance(val, ValStr):
|
||||||
|
raise CentvrionError(f"MAIVSCVLA expects a string, got {type(val).__name__}")
|
||||||
|
s = val.value()
|
||||||
|
return vtable, ValStr("".join(chr(ord(c) - 32) if "a" <= c <= "z" else c for c in s))
|
||||||
|
case "MINVSCVLA":
|
||||||
|
if len(params) != 1:
|
||||||
|
raise CentvrionError("MINVSCVLA takes exactly I argument")
|
||||||
|
val = params[0]
|
||||||
|
if not isinstance(val, ValStr):
|
||||||
|
raise CentvrionError(f"MINVSCVLA expects a string, got {type(val).__name__}")
|
||||||
|
s = val.value()
|
||||||
|
return vtable, ValStr("".join(chr(ord(c) + 32) if "A" <= c <= "Z" else c for c in s))
|
||||||
case "CLAVES":
|
case "CLAVES":
|
||||||
if not isinstance(params[0], ValDict):
|
if not isinstance(params[0], ValDict):
|
||||||
raise CentvrionError("CLAVES requires a dict")
|
raise CentvrionError("CLAVES requires a dict")
|
||||||
|
|||||||
@@ -240,6 +240,20 @@ def _emit_builtin(node, ctx):
|
|||||||
case "LITTERA":
|
case "LITTERA":
|
||||||
lines.append(f"CentValue {tmp} = cent_littera({param_vars[0]});")
|
lines.append(f"CentValue {tmp} = cent_littera({param_vars[0]});")
|
||||||
|
|
||||||
|
case "MAIVSCVLA":
|
||||||
|
if len(param_vars) != 1:
|
||||||
|
lines.append(f'cent_runtime_error("MAIVSCVLA takes exactly I argument");')
|
||||||
|
lines.append(f"CentValue {tmp} = cent_null();")
|
||||||
|
else:
|
||||||
|
lines.append(f"CentValue {tmp} = cent_maivscvla({param_vars[0]});")
|
||||||
|
|
||||||
|
case "MINVSCVLA":
|
||||||
|
if len(param_vars) != 1:
|
||||||
|
lines.append(f'cent_runtime_error("MINVSCVLA takes exactly I argument");')
|
||||||
|
lines.append(f"CentValue {tmp} = cent_null();")
|
||||||
|
else:
|
||||||
|
lines.append(f"CentValue {tmp} = cent_minvscvla({param_vars[0]});")
|
||||||
|
|
||||||
case "FORTVITVS_NVMERVS":
|
case "FORTVITVS_NVMERVS":
|
||||||
if not ctx.has_module("FORS"):
|
if not ctx.has_module("FORS"):
|
||||||
lines.append('cent_runtime_error("FORS module required for FORTVITVS_NVMERVS");')
|
lines.append('cent_runtime_error("FORS module required for FORTVITVS_NVMERVS");')
|
||||||
|
|||||||
@@ -671,6 +671,30 @@ CentValue cent_littera(CentValue v) {
|
|||||||
return cent_str(cent_make_string(v));
|
return cent_str(cent_make_string(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CentValue cent_maivscvla(CentValue v) {
|
||||||
|
if (v.type != CENT_STR) cent_type_error("'MAIVSCVLA' requires a string");
|
||||||
|
size_t len = strlen(v.sval);
|
||||||
|
char *out = cent_arena_alloc(cent_arena, len + 1);
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
unsigned char c = (unsigned char)v.sval[i];
|
||||||
|
out[i] = (c >= 'a' && c <= 'z') ? (char)(c - ('a' - 'A')) : (char)c;
|
||||||
|
}
|
||||||
|
out[len] = '\0';
|
||||||
|
return cent_str(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
CentValue cent_minvscvla(CentValue v) {
|
||||||
|
if (v.type != CENT_STR) cent_type_error("'MINVSCVLA' requires a string");
|
||||||
|
size_t len = strlen(v.sval);
|
||||||
|
char *out = cent_arena_alloc(cent_arena, len + 1);
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
unsigned char c = (unsigned char)v.sval[i];
|
||||||
|
out[i] = (c >= 'A' && c <= 'Z') ? (char)(c + ('a' - 'A')) : (char)c;
|
||||||
|
}
|
||||||
|
out[len] = '\0';
|
||||||
|
return cent_str(out);
|
||||||
|
}
|
||||||
|
|
||||||
CentValue cent_typvs(CentValue v) {
|
CentValue cent_typvs(CentValue v) {
|
||||||
switch (v.type) {
|
switch (v.type) {
|
||||||
case CENT_INT: return cent_str("NVMERVS");
|
case CENT_INT: return cent_str("NVMERVS");
|
||||||
|
|||||||
@@ -228,6 +228,8 @@ CentValue cent_avdi(void); /* AVDI */
|
|||||||
CentValue cent_avdi_numerus(void); /* AVDI_NVMERVS */
|
CentValue cent_avdi_numerus(void); /* AVDI_NVMERVS */
|
||||||
CentValue cent_longitudo(CentValue v); /* LONGITVDO */
|
CentValue cent_longitudo(CentValue v); /* LONGITVDO */
|
||||||
CentValue cent_littera(CentValue v); /* LITTERA */
|
CentValue cent_littera(CentValue v); /* LITTERA */
|
||||||
|
CentValue cent_maivscvla(CentValue v); /* MAIVSCVLA */
|
||||||
|
CentValue cent_minvscvla(CentValue v); /* MINVSCVLA */
|
||||||
CentValue cent_fortuitus_numerus(CentValue lo, CentValue hi); /* FORTVITVS_NVMERVS */
|
CentValue cent_fortuitus_numerus(CentValue lo, CentValue hi); /* FORTVITVS_NVMERVS */
|
||||||
CentValue cent_fortuita_electionis(CentValue lst); /* FORTVITA_ELECTIO */
|
CentValue cent_fortuita_electionis(CentValue lst); /* FORTVITA_ELECTIO */
|
||||||
CentValue cent_decimatio(CentValue lst); /* DECIMATIO */
|
CentValue cent_decimatio(CentValue lst); /* DECIMATIO */
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ builtin_tokens = [("BUILTIN", i) for i in [
|
|||||||
"FORTVITA_ELECTIO",
|
"FORTVITA_ELECTIO",
|
||||||
"LITTERA",
|
"LITTERA",
|
||||||
"LONGITVDO",
|
"LONGITVDO",
|
||||||
|
"MAIVSCVLA",
|
||||||
|
"MINVSCVLA",
|
||||||
"NVMERVS",
|
"NVMERVS",
|
||||||
"ORDINA",
|
"ORDINA",
|
||||||
"SEMEN",
|
"SEMEN",
|
||||||
@@ -108,8 +110,8 @@ whitespace_tokens = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
all_tokens = (
|
all_tokens = (
|
||||||
keyword_tokens +
|
|
||||||
builtin_tokens +
|
builtin_tokens +
|
||||||
|
keyword_tokens +
|
||||||
module_tokens +
|
module_tokens +
|
||||||
data_tokens +
|
data_tokens +
|
||||||
symbol_tokens +
|
symbol_tokens +
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ contexts:
|
|||||||
scope: constant.language.centvrion
|
scope: constant.language.centvrion
|
||||||
|
|
||||||
builtins:
|
builtins:
|
||||||
- match: '\b(ADIVNGE|AVDI_NVMERVS|AVDI|AVSCVLTA|CLAVES|DECIMATIO|DIC|DORMI|EVERRE|FORTVITVS_NVMERVS|FORTVITA_ELECTIO|LEGE|LITTERA|LONGITVDO|NVMERVS|ORDINA|PETE|PETITVR|QVAERE|SCINDE|SCRIBE|SEMEN|SENATVS|SVBSTITVE|TYPVS)\b'
|
- match: '\b(ADIVNGE|AVDI_NVMERVS|AVDI|AVSCVLTA|CLAVES|DECIMATIO|DIC|DORMI|EVERRE|FORTVITVS_NVMERVS|FORTVITA_ELECTIO|LEGE|LITTERA|LONGITVDO|MAIVSCVLA|MINVSCVLA|NVMERVS|ORDINA|PETE|PETITVR|QVAERE|SCINDE|SCRIBE|SEMEN|SENATVS|SVBSTITVE|TYPVS)\b'
|
||||||
scope: support.function.builtin.centvrion
|
scope: support.function.builtin.centvrion
|
||||||
|
|
||||||
modules:
|
modules:
|
||||||
|
|||||||
36
tests.py
36
tests.py
@@ -740,6 +740,36 @@ builtin_tests = [
|
|||||||
('SCINDE(",a,", ",")', Program([], [ExpressionStatement(BuiltIn("SCINDE", [String(",a,"), String(",")]))]), ValList([ValStr(""), ValStr("a"), ValStr("")])),
|
('SCINDE(",a,", ",")', Program([], [ExpressionStatement(BuiltIn("SCINDE", [String(",a,"), String(",")]))]), ValList([ValStr(""), ValStr("a"), ValStr("")])),
|
||||||
# SCINDE: empty delimiter (split into chars)
|
# SCINDE: empty delimiter (split into chars)
|
||||||
('SCINDE("abc", "")', Program([], [ExpressionStatement(BuiltIn("SCINDE", [String("abc"), String("")]))]), ValList([ValStr("a"), ValStr("b"), ValStr("c")])),
|
('SCINDE("abc", "")', Program([], [ExpressionStatement(BuiltIn("SCINDE", [String("abc"), String("")]))]), ValList([ValStr("a"), ValStr("b"), ValStr("c")])),
|
||||||
|
# MAIVSCVLA: basic lowercase→uppercase
|
||||||
|
('MAIVSCVLA("hello")', Program([], [ExpressionStatement(BuiltIn("MAIVSCVLA", [String("hello")]))]), ValStr("HELLO")),
|
||||||
|
# MAIVSCVLA: mixed case
|
||||||
|
('MAIVSCVLA("HeLLo")', Program([], [ExpressionStatement(BuiltIn("MAIVSCVLA", [String("HeLLo")]))]), ValStr("HELLO")),
|
||||||
|
# MAIVSCVLA: already uppercase (idempotence)
|
||||||
|
('MAIVSCVLA("HELLO")', Program([], [ExpressionStatement(BuiltIn("MAIVSCVLA", [String("HELLO")]))]), ValStr("HELLO")),
|
||||||
|
# MAIVSCVLA: empty string
|
||||||
|
('MAIVSCVLA("")', Program([], [ExpressionStatement(BuiltIn("MAIVSCVLA", [String("")]))]), ValStr("")),
|
||||||
|
# MAIVSCVLA: Roman-numeral-shaped ASCII (case-only, not numeral-aware)
|
||||||
|
('MAIVSCVLA("xii")', Program([], [ExpressionStatement(BuiltIn("MAIVSCVLA", [String("xii")]))]), ValStr("XII")),
|
||||||
|
# MAIVSCVLA: non-alphabetic chars unchanged
|
||||||
|
('MAIVSCVLA("a,b!1")', Program([], [ExpressionStatement(BuiltIn("MAIVSCVLA", [String("a,b!1")]))]), ValStr("A,B!1")),
|
||||||
|
# MAIVSCVLA: via variable
|
||||||
|
('DESIGNA s VT "foo"\nDIC(MAIVSCVLA(s))', Program([], [Designa(ID("s"), String("foo")), ExpressionStatement(BuiltIn("DIC", [BuiltIn("MAIVSCVLA", [ID("s")])]))]), ValStr("FOO"), "FOO\n"),
|
||||||
|
# MAIVSCVLA: concatenated with &
|
||||||
|
('MAIVSCVLA("hi") & "!"', Program([], [ExpressionStatement(BinOp(BuiltIn("MAIVSCVLA", [String("hi")]), String("!"), "SYMBOL_AMPERSAND"))]), ValStr("HI!")),
|
||||||
|
# MINVSCVLA: basic uppercase→lowercase
|
||||||
|
('MINVSCVLA("HELLO")', Program([], [ExpressionStatement(BuiltIn("MINVSCVLA", [String("HELLO")]))]), ValStr("hello")),
|
||||||
|
# MINVSCVLA: mixed case
|
||||||
|
('MINVSCVLA("HeLLo")', Program([], [ExpressionStatement(BuiltIn("MINVSCVLA", [String("HeLLo")]))]), ValStr("hello")),
|
||||||
|
# MINVSCVLA: already lowercase (idempotence)
|
||||||
|
('MINVSCVLA("hello")', Program([], [ExpressionStatement(BuiltIn("MINVSCVLA", [String("hello")]))]), ValStr("hello")),
|
||||||
|
# MINVSCVLA: empty string
|
||||||
|
('MINVSCVLA("")', Program([], [ExpressionStatement(BuiltIn("MINVSCVLA", [String("")]))]), ValStr("")),
|
||||||
|
# MINVSCVLA: Roman-numeral-shaped ASCII (case-only, not numeral-aware)
|
||||||
|
('MINVSCVLA("XII")', Program([], [ExpressionStatement(BuiltIn("MINVSCVLA", [String("XII")]))]), ValStr("xii")),
|
||||||
|
# MINVSCVLA: non-alphabetic chars unchanged
|
||||||
|
('MINVSCVLA("A,B!1")', Program([], [ExpressionStatement(BuiltIn("MINVSCVLA", [String("A,B!1")]))]), ValStr("a,b!1")),
|
||||||
|
# MINVSCVLA round-trips MAIVSCVLA on lowercase input
|
||||||
|
('MINVSCVLA(MAIVSCVLA("hi"))', Program([], [ExpressionStatement(BuiltIn("MINVSCVLA", [BuiltIn("MAIVSCVLA", [String("hi")])]))]), ValStr("hi")),
|
||||||
]
|
]
|
||||||
|
|
||||||
class TestBuiltins(unittest.TestCase):
|
class TestBuiltins(unittest.TestCase):
|
||||||
@@ -850,6 +880,12 @@ error_tests = [
|
|||||||
("QVAERE('a{3}', 'aaa')", CentvrionError), # Arabic quantifier in pattern
|
("QVAERE('a{3}', 'aaa')", CentvrionError), # Arabic quantifier in pattern
|
||||||
('SCINDE(I, ",")', CentvrionError), # SCINDE requires strings, not int
|
('SCINDE(I, ",")', CentvrionError), # SCINDE requires strings, not int
|
||||||
('SCINDE("a", I)', CentvrionError), # SCINDE requires strings, not int delimiter
|
('SCINDE("a", I)', CentvrionError), # SCINDE requires strings, not int delimiter
|
||||||
|
('MAIVSCVLA(I)', CentvrionError), # MAIVSCVLA requires a string, not int
|
||||||
|
('MAIVSCVLA()', CentvrionError), # MAIVSCVLA requires exactly 1 arg
|
||||||
|
('MAIVSCVLA("a", "b")', CentvrionError), # MAIVSCVLA too many args
|
||||||
|
('MINVSCVLA(I)', CentvrionError), # MINVSCVLA requires a string, not int
|
||||||
|
('MINVSCVLA()', CentvrionError), # MINVSCVLA requires exactly 1 arg
|
||||||
|
('MINVSCVLA("a", "b")', CentvrionError), # MINVSCVLA too many args
|
||||||
('PETE("http://example.com")', CentvrionError), # RETE required for PETE
|
('PETE("http://example.com")', CentvrionError), # RETE required for PETE
|
||||||
('CVM RETE\nPETE(I)', CentvrionError), # PETE requires a string URL
|
('CVM RETE\nPETE(I)', CentvrionError), # PETE requires a string URL
|
||||||
('PETITVR("/", FVNCTIO (r) VT {\nREDI("hi")\n})', CentvrionError), # RETE required for PETITVR
|
('PETITVR("/", FVNCTIO (r) VT {\nREDI("hi")\n})', CentvrionError), # RETE required for PETITVR
|
||||||
|
|||||||
@@ -74,6 +74,8 @@
|
|||||||
"LEGE": { "prefix": "LEGE", "body": "LEGE", "description": "read file contents (SCRIPTA module)" },
|
"LEGE": { "prefix": "LEGE", "body": "LEGE", "description": "read file contents (SCRIPTA module)" },
|
||||||
"LITTERA": { "prefix": "LITTERA", "body": "LITTERA", "description": "coerce any value to its display string" },
|
"LITTERA": { "prefix": "LITTERA", "body": "LITTERA", "description": "coerce any value to its display string" },
|
||||||
"LONGITVDO": { "prefix": "LONGITVDO", "body": "LONGITVDO", "description": "length of array, string, or dict" },
|
"LONGITVDO": { "prefix": "LONGITVDO", "body": "LONGITVDO", "description": "length of array, string, or dict" },
|
||||||
|
"MAIVSCVLA": { "prefix": "MAIVSCVLA", "body": "MAIVSCVLA", "description": "uppercase a string (ASCII a-z → A-Z)" },
|
||||||
|
"MINVSCVLA": { "prefix": "MINVSCVLA", "body": "MINVSCVLA", "description": "lowercase a string (ASCII A-Z → a-z)" },
|
||||||
"NVMERVS": { "prefix": "NVMERVS", "body": "NVMERVS", "description": "parse a Roman numeral string to an integer" },
|
"NVMERVS": { "prefix": "NVMERVS", "body": "NVMERVS", "description": "parse a Roman numeral string to an integer" },
|
||||||
"ORDINA": { "prefix": "ORDINA", "body": "ORDINA", "description": "sort an array in ascending order" },
|
"ORDINA": { "prefix": "ORDINA", "body": "ORDINA", "description": "sort an array in ascending order" },
|
||||||
"PETE": { "prefix": "PETE", "body": "PETE", "description": "HTTP GET request (RETE module)" },
|
"PETE": { "prefix": "PETE", "body": "PETE", "description": "HTTP GET request (RETE module)" },
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"name": "support.function.builtin.cent",
|
"name": "support.function.builtin.cent",
|
||||||
"match": "\\b(ADIVNGE|AVDI_NVMERVS|AVDI|AVSCVLTA|CLAVES|DECIMATIO|DIC|DORMI|EVERRE|FORTVITVS_NVMERVS|FORTVITA_ELECTIO|LEGE|LITTERA|LONGITVDO|NVMERVS|ORDINA|PETE|PETITVR|QVAERE|SCINDE|SCRIBE|SEMEN|SENATVS|SVBSTITVE|TYPVS)\\b"
|
"match": "\\b(ADIVNGE|AVDI_NVMERVS|AVDI|AVSCVLTA|CLAVES|DECIMATIO|DIC|DORMI|EVERRE|FORTVITVS_NVMERVS|FORTVITA_ELECTIO|LEGE|LITTERA|LONGITVDO|MAIVSCVLA|MINVSCVLA|NVMERVS|ORDINA|PETE|PETITVR|QVAERE|SCINDE|SCRIBE|SEMEN|SENATVS|SVBSTITVE|TYPVS)\\b"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user