aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2025-03-14 23:44:10 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-15 14:14:04 +0000
commit79696a272d1d8f2959a0b9cfdd8487d356416a05 (patch)
tree0d6e28f00b6a62e2fd7488b1df772305f56a812a
parentsomewhat clean up alv.parsing (diff)
downloadalive-79696a272d1d8f2959a0b9cfdd8487d356416a05.tar.gz
alive-79696a272d1d8f2959a0b9cfdd8487d356416a05.zip
implement template strings
-rw-r--r--alv/ast.moon2
-rw-r--r--alv/base/builtin.moon12
-rw-r--r--alv/base/init.moon3
-rw-r--r--alv/cell.moon8
-rw-r--r--alv/parsing.moon14
-rw-r--r--alv/template_string.moon83
-rw-r--r--spec/internal/parsing_spec.moon10
7 files changed, 118 insertions, 14 deletions
diff --git a/alv/ast.moon b/alv/ast.moon
index 04e6627..4d0ecae 100644
--- a/alv/ast.moon
+++ b/alv/ast.moon
@@ -33,6 +33,7 @@
-- @treturn string the exact string this Node was parsed from
import Cell, RootCell from require 'alv.cell'
+import TemplateString from require 'alv.template_string'
import Constant from require 'alv.result.const'
import Dummy from require 'alv.dummy'
import Tag from require 'alv.tag'
@@ -40,6 +41,7 @@ import Tag from require 'alv.tag'
{
:Cell
:RootCell
+ :TemplateString
:Constant
:Dummy
:Tag
diff --git a/alv/base/builtin.moon b/alv/base/builtin.moon
index 558a65a..4b8ee5a 100644
--- a/alv/base/builtin.moon
+++ b/alv/base/builtin.moon
@@ -6,6 +6,7 @@
-- See `builtin` and `invoke` for examples.
--
-- @classmod Builtin
+import TemplateString from require 'alv.template_string'
class Builtin
--- Builtin interface.
@@ -107,7 +108,16 @@ class Builtin
else
builtin\setup nil
- builtin\eval scope, cell\tail!
+ tail = {}
+ for child in *cell\tail!
+ if child.__class == TemplateString
+ table.insert tail, child.string
+ for inner in *child.children
+ table.insert tail, inner
+ else
+ table.insert tail, child
+
+ builtin\eval scope, tail
__tostring: => "<#{@@__name}[#{@tag}] #{@head}>"
__inherited: (cls) => cls.__base.__tostring = @__tostring
diff --git a/alv/base/init.moon b/alv/base/init.moon
index bfe9123..a5d0b88 100644
--- a/alv/base/init.moon
+++ b/alv/base/init.moon
@@ -20,6 +20,7 @@
-- @see type.Primitive
-- @see type.Array
-- @see type.Struct
+-- @see TemplateString
-- @see RTNode
-- @see Error
@@ -31,6 +32,7 @@ import Input from require 'alv.base.input'
import const, sig, evt, any from require 'alv.base.match'
import Constant, SigStream, EvtStream from require 'alv.result'
import T, Primitive, Array, Struct from require 'alv.type'
+import TemplateString from require 'alv.template_string'
import RTNode from require 'alv.rtnode'
import Error from require 'alv.error'
@@ -49,6 +51,7 @@ import Error from require 'alv.error'
-- Types
:T, :Primitive, :Array, :Struct
+ :TemplateString
:RTNode
:Error
}
diff --git a/alv/cell.moon b/alv/cell.moon
index ced9032..7b6cf61 100644
--- a/alv/cell.moon
+++ b/alv/cell.moon
@@ -98,7 +98,7 @@ class Cell
clone: (parent) =>
tag = @tag\clone parent
children = [child\clone parent for child in *@children]
- Cell tag, children, @white
+ @@ tag, children, @white
--- stringify this Cell.
--
@@ -155,11 +155,6 @@ class RootCell extends Cell
head: => Constant.sym 'do'
tail: => @children
- clone: (parent) =>
- tag = @tag\clone parent
- children = [child\clone parent for child in *@children]
- RootCell tag, children, @white
-
stringify: =>
buf = ''
buf ..= @white[0]
@@ -184,4 +179,5 @@ class RootCell extends Cell
{
:Cell
:RootCell
+ :TemplateString
}
diff --git a/alv/parsing.moon b/alv/parsing.moon
index 7174c06..d5ec313 100644
--- a/alv/parsing.moon
+++ b/alv/parsing.moon
@@ -2,7 +2,7 @@
-- Lpeg Grammar for parsing `alive` code.
--
-- @module parsing
-import Cell, RootCell, Constant, Tag from require 'alv.ast'
+import Cell, RootCell, Constant, TemplateString, Tag from require 'alv.ast'
import R, S, P, V, C, Ct from require 'lpeg'
-- whitespace
@@ -30,21 +30,25 @@ float = (digit^1 * '.' * digit^0) + (digit^0 * '.' * digit^1)
num = ((P '-')^-1 * (float + fract + int)) / Constant\parse 'num'
atom = num + sym + str
-
expr = (V 'cell') + atom
-explist = Ct mspace * ((V 'expr') * (space * (V 'expr'))^0 * mspace)^-1
+
+tplcont = ((P '\\"') + (P '\\\\') + (1 - (P '"') - (P '#')))^1 / 1
+tplstr = (P '#"') * ((P '##') / 1 + ('#' * expr) + tplcont)^0 * '"' / TemplateString\parse
+
+expitem = tplstr + expr
+explist = Ct mspace * (expitem * (space * expitem)^0 * mspace)^-1
tag = (P '[') * (digit^1 / Tag.parse) * (P ']')
cell = (P '(') * tag^-1 * explist * (P ')') / Cell.parse
root = P {
explist / RootCell.parse
- :expr, :cell
+ :cell
}
cell = P {
'cell'
- :expr, :cell
+ :cell
}
program = root * -1
diff --git a/alv/template_string.moon b/alv/template_string.moon
new file mode 100644
index 0000000..bd6ae0f
--- /dev/null
+++ b/alv/template_string.moon
@@ -0,0 +1,83 @@
+----
+-- A string with substitutions, partially implements the `AST` interface.
+--
+-- Is automatically "splatted" into multiple expressions when the containing
+-- `Cell` is evaluated: the original string (str=), but with with substitution
+-- markers like `#{1}`, `#{2}` and so on and one expression for each substitution.
+--
+-- `#` symbols in the original string are escaped to `##`.
+--
+-- @classmod TemplateString
+import Constant from require 'alv.result'
+
+-- @type TemplateString
+class TemplateString
+--- AST interface
+--
+-- `TemplateString` partially implements the `AST` interface.
+-- @section ast
+
+ --- create a clone with its own identity.
+ --
+ -- creates a clone of this TemplateString by cloning all child expressions
+ -- recursively.
+ --
+ -- @tparam Tag parent
+ -- @treturn TemplateString
+ clone: (parent) =>
+ children = [child\clone parent for child in *@children]
+ @@ @string, children
+
+ --- stringify this TemplateString.
+ --
+ -- if `depth` is passed, does not faithfully recreate the original string but
+ -- rather create useful debug output.
+ --
+ -- @tparam[opt] int depth the maximum depth, defaults to infinite
+ -- @treturn string the exact string this TemplateString was parsed from
+ stringify: (depth=-1) =>
+ children = ['#' .. child\stringify depth for child in *@children]
+ str = @@.subst @.string!, (i) -> '#' .. @children[i]\stringify depth
+ '#"' .. str .. '"'
+
+--- static functions
+-- @section static
+
+ new: (@string, @children) =>
+
+ --- apply substitutions to a template string.
+ --
+ -- This also reverses the escaping that the parser applied.
+ --
+ -- @classmethod
+ -- @tparam string str the evaluated `TemplateString.string`
+ -- @tparam function fn function called with index i to obtain string to substitute
+ -- @treturn string
+ @subst: (str, fn) ->
+ str = str\gsub '##', '#'
+ str\gsub '#{(%d+)}', (i) -> fn tonumber i
+
+ --- parse a TemplateString (for parsing with Lpeg).
+ --
+ -- @classmethod
+ -- @tparam {string|AST,...} pieces
+ -- @treturn TemplateString
+ @parse: (...) =>
+ string = ''
+ children = {}
+
+ i = 1
+ for elem in *{...}
+ string ..= if elem == '##' then elem
+ elseif 'string' == type elem then elem\gsub '#', '##'
+ else
+ table.insert children, elem
+ '#{' .. #children .. '}'
+
+ string = Constant.str string
+
+ @@ string, children
+
+{
+ :TemplateString
+}
diff --git a/spec/internal/parsing_spec.moon b/spec/internal/parsing_spec.moon
index 09d5b85..534155f 100644
--- a/spec/internal/parsing_spec.moon
+++ b/spec/internal/parsing_spec.moon
@@ -1,5 +1,4 @@
-import space, atom, expr, explist, cell, program, comment
- from require 'alv.parsing'
+import space, atom, cell, program, comment from require 'alv.parsing'
import Constant from require 'alv'
import Logger from require 'alv.logger'
Logger\init 'silent'
@@ -91,6 +90,13 @@ describe 'Cell', ->
assert.is.equal 2, #node.children
assert.is.equal 42, node.tag.value
+ test 'template strings', ->
+ node = verify_parse cell, '( hi #" string with #3#5 some #(= "contents") "
+ "friend" )'
+
+ assert.is.equal 3, #node.children
+ assert.is.equal (Constant.str 'friend'), node.children[3]
+
describe 'RootCell parsing', ->
describe 'handles whitespace', ->
verify = (str) ->