From 02fa9d075d680fa183c89fb016b7fd4637386e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Reuh=20Fildadut?= Date: Wed, 29 May 2024 14:14:59 +0200 Subject: [PATCH] [internal] clean up format calls in interpolations, add format(val::is string) without quotes --- anselme/ast/StringInterpolation.lua | 8 ++++---- anselme/ast/TextInterpolation.lua | 6 +++--- anselme/ast/Typed.lua | 17 ----------------- anselme/ast/abstract/Node.lua | 19 ++++++++++++++++--- anselme/stdlib/base.lua | 8 +++++++- anselme/stdlib/string.lua | 6 ++++++ doc/language.md | 4 ++-- test/results/custom text formatting.ans | 2 +- 8 files changed, 39 insertions(+), 31 deletions(-) diff --git a/anselme/ast/StringInterpolation.lua b/anselme/ast/StringInterpolation.lua index 4b80286..9b290fb 100644 --- a/anselme/ast/StringInterpolation.lua +++ b/anselme/ast/StringInterpolation.lua @@ -36,10 +36,10 @@ local StringInterpolation = ast.abstract.Node { local t = {} for _, e in ipairs(self.list) do local r = e:eval(state) - if String:is(r) then - r = r.string - else - r = r:format(state) + if String:is(e) then -- raw string + r = e.string + else -- interpolation + r = e:format_custom(state) end table.insert(t, r) end diff --git a/anselme/ast/TextInterpolation.lua b/anselme/ast/TextInterpolation.lua index b93d2a5..ac92c23 100644 --- a/anselme/ast/TextInterpolation.lua +++ b/anselme/ast/TextInterpolation.lua @@ -39,14 +39,14 @@ local TextInterpolation = ast.abstract.Node { local tags = tag_manager:get(state) for _, e in ipairs(self.list) do local r = e:eval(state) - if String:is(r) then + if String:is(e) then -- raw string t:insert(r, tags) - elseif Text:is(r) then + elseif Text:is(r) then -- interpolation for _, v in ipairs(r.list) do t:insert(v[1], v[2]) end else - t:insert(String:new(r:format(state)), tags) + t:insert(String:new(r:format_custom(state)), tags) end end return t diff --git a/anselme/ast/Typed.lua b/anselme/ast/Typed.lua index 1ea7c72..db3ef0c 100644 --- a/anselme/ast/Typed.lua +++ b/anselme/ast/Typed.lua @@ -2,10 +2,6 @@ local ast = require("anselme.ast") local operator_priority = require("anselme.common").operator_priority -local format_identifier - -local ArgumentTuple - local Typed Typed = ast.abstract.Runtime { type = "typed", @@ -19,15 +15,6 @@ Typed = ast.abstract.Runtime { end, _format = function(self, state, prio, ...) - -- try custom format - if state and state.scope:defined(format_identifier) then - local custom_format = format_identifier:eval(state) - local args = ArgumentTuple:new(self) - local fn, d_args = custom_format:dispatch(state, args) - if fn then - return custom_format:call(state, d_args):format(state, prio, ...) - end - end return ("type(%s, %s)"):format(self.expression:format(state, operator_priority["_,_"], ...), self.type_expression:format_right(state, operator_priority["_,_"], ...)) end, @@ -37,8 +24,4 @@ Typed = ast.abstract.Runtime { end } -package.loaded[...] = Typed -format_identifier = ast.Identifier:new("format") -ArgumentTuple = ast.ArgumentTuple - return Typed diff --git a/anselme/ast/abstract/Node.lua b/anselme/ast/abstract/Node.lua index add4d48..d5a0d8b 100644 --- a/anselme/ast/abstract/Node.lua +++ b/anselme/ast/abstract/Node.lua @@ -14,10 +14,10 @@ local unpack = table.unpack or unpack local uuid = require("anselme.common").uuid -local Call, Identifier, Struct, Tuple, String, Pair +local Call, Identifier, Struct, Tuple, String, Pair, ArgumentTuple local resume_manager -local custom_call_identifier +local custom_call_identifier, custom_format_identifier local context_max_length = 50 local function cutoff_text(str) @@ -372,6 +372,18 @@ Node = class { _format_priority_cache = nil, -- cached priority from_symbol = nil, -- last symbol this node was retrived from + -- return a human-readable, string representation of the node + -- this works by calling the `format(node)` anselme method (if the method returns a non-string value, it is converted to a string using :format) + format_custom = function(self, state) + local custom_format = custom_format_identifier:eval(state) + local r = custom_format:call(state, ArgumentTuple:new(self)) + if String:is(r) then + return r.string + else + return r:format(state) + end + end, + -- return Lua value -- this should probably be only called on a Node that is already evaluated -- redefine if you want, probably only for nodes that are already evaluated @@ -430,8 +442,9 @@ Node = class { -- Thus, any require here that may require other Nodes shall be done here. This method is called in anselme.lua after everything else is required. _i_hate_cycles = function(self) local ast = require("anselme.ast") - Call, Identifier, Struct, Tuple, String, Pair = ast.Call, ast.Identifier, ast.Struct, ast.Tuple, ast.String, ast.Pair + Call, Identifier, Struct, Tuple, String, Pair, ArgumentTuple = ast.Call, ast.Identifier, ast.Struct, ast.Tuple, ast.String, ast.Pair, ast.ArgumentTuple custom_call_identifier = Identifier:new("_!") + custom_format_identifier = Identifier:new("format") resume_manager = require("anselme.state.resume_manager") end, diff --git a/anselme/stdlib/base.lua b/anselme/stdlib/base.lua index 41ea762..e548100 100644 --- a/anselme/stdlib/base.lua +++ b/anselme/stdlib/base.lua @@ -5,12 +5,18 @@ return { { "print", "(a)", function(state, a) - print(a:format(state)) + print(a:format_custom(state)) return Nil:new() end }, { "hash", "(a)", + "format", "(val)", + function(state, val) + return String:new(val:format(state)) + end + }, + { function(state, a) return String:new(a:hash()) end diff --git a/anselme/stdlib/string.lua b/anselme/stdlib/string.lua index f89d6b0..c4e8b2e 100644 --- a/anselme/stdlib/string.lua +++ b/anselme/stdlib/string.lua @@ -9,5 +9,11 @@ return { function(state, s) return Number:new(utf8.len(s.string)) end + }, + { + "format", "(val::is string)", + function(state, val) + return val + end } } diff --git a/doc/language.md b/doc/language.md index cf6d4c2..f44898c 100644 --- a/doc/language.md +++ b/doc/language.md @@ -485,7 +485,7 @@ Strings literal starts with a `"` and end with a `"`. Specials characters, inclu * `\n` is replaced with a newline * `\t` is replaced with a tab character -Non-escaped `{` in the string start an interpolated expression; any expression can then follow, but a closing `}` is expected after it. When the string literal is evaluated, each interpolated expression will be evaluated, the returned value will be formatted to a string and appear in the string where the `{` expression `}` appeared in the literal. +Non-escaped `{` in the string start an interpolated expression; any expression can then follow, but a closing `}` is expected after it. When the string literal is evaluated, each interpolated expression will be evaluated, the returned value will be converted to a string using `format` and appear in the string where the `{` expression `}` appeared in the literal. Strings can contain newlines. @@ -680,7 +680,7 @@ var!type // returns "$" var!value // returns 10 ``` -When trying to convert the custom type to a string, for example in a string interpolation, the `format(custom type)` function call will be tried. If the function can be called, its return value will be used as the string representation of the value. +When trying to convert the custom type to a string, for example in a string interpolation, the `format(custom type)` function will be called. How custom types appear in strings can therefore be changed by overloading the `format` function. ``` :var = type(10, "$") diff --git a/test/results/custom text formatting.ans b/test/results/custom text formatting.ans index 272e6ed..c628d93 100644 --- a/test/results/custom text formatting.ans +++ b/test/results/custom text formatting.ans @@ -1,6 +1,6 @@ --# run #-- --- text --- -| {}"" {}"\"Name: Darmanin\nAge: 38\"" {}"" | +| {}"" {}"Name: Darmanin\nAge: 38" {}"" | --- return --- () --# saved #--