diff options
| author | s-ol <s+removethis@s-ol.nu> | 2025-03-14 23:44:10 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-15 14:14:04 +0000 |
| commit | 79696a272d1d8f2959a0b9cfdd8487d356416a05 (patch) | |
| tree | 0d6e28f00b6a62e2fd7488b1df772305f56a812a | |
| parent | somewhat clean up alv.parsing (diff) | |
| download | alive-79696a272d1d8f2959a0b9cfdd8487d356416a05.tar.gz alive-79696a272d1d8f2959a0b9cfdd8487d356416a05.zip | |
implement template strings
| -rw-r--r-- | alv/ast.moon | 2 | ||||
| -rw-r--r-- | alv/base/builtin.moon | 12 | ||||
| -rw-r--r-- | alv/base/init.moon | 3 | ||||
| -rw-r--r-- | alv/cell.moon | 8 | ||||
| -rw-r--r-- | alv/parsing.moon | 14 | ||||
| -rw-r--r-- | alv/template_string.moon | 83 | ||||
| -rw-r--r-- | spec/internal/parsing_spec.moon | 10 |
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) -> |
