Skip to content

Commit

Permalink
Refactor snippets
Browse files Browse the repository at this point in the history
  • Loading branch information
peachpit-site committed Feb 2, 2025
1 parent 60c57b1 commit 340382b
Show file tree
Hide file tree
Showing 29 changed files with 201 additions and 303 deletions.
10 changes: 6 additions & 4 deletions examples/test.pf
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ def
foo(i int) :
"foo"


zort(t ... any?) :
foo +

troz(i, j, k int) :
"troz"

newtype

gorp = clone snippet

var

sn = gorp -- foo

T tuple = ()
U tuple = 1, 2, 3
V tuple = tuple(1)
Expand Down
9 changes: 9 additions & 0 deletions source/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,15 @@ func (rl *RuneLiteral) Children() []Node { return []Node{} }
func (rl *RuneLiteral) GetToken() *token.Token { return &rl.Token }
func (rl *RuneLiteral) String() string { return strconv.QuoteRune(rl.Value) }

type SnippetLiteral struct {
Token token.Token
Value string
}

func (sl *SnippetLiteral) Children() []Node { return []Node{} }
func (sl *SnippetLiteral) GetToken() *token.Token { return &sl.Token }
func (sl *SnippetLiteral) String() string { return "---" + sl.Value }

type StringLiteral struct {
Token token.Token
Value string
Expand Down
21 changes: 15 additions & 6 deletions source/compiler/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var BUILTINS = map[string]functionAndReturnType{
"cast_to_map": {(*Compiler).btCastToMap, AltType(values.MAP)},
"cast_to_pair": {(*Compiler).btCastToPair, AltType(values.PAIR)},
"cast_to_set": {(*Compiler).btCastToSet, AltType(values.SET)},
"cast_to_snippet": {(*Compiler).btCastToSnippet, AltType(values.SNIPPET)},
"cast_to_string": {(*Compiler).btCastToString, AltType(values.STRING)},
"codepoint": {(*Compiler).btCodepoint, AltType(values.INT)},
"divide_floats": {(*Compiler).btDivideFloats, AltType(values.ERROR, values.FLOAT)},
Expand Down Expand Up @@ -54,6 +55,7 @@ var BUILTINS = map[string]functionAndReturnType{
"len_list": {(*Compiler).btLenList, AltType(values.INT)},
"len_map": {(*Compiler).btLenMap, AltType(values.INT)},
"len_set": {(*Compiler).btLenSet, AltType(values.INT)},
"len_snippet": {(*Compiler).btLenSnippet, AltType(values.INT)},
"len_string": {(*Compiler).btLenString, AltType(values.INT)},
"len_tuple": {(*Compiler).btLenTuple, AltType(values.INT)},
"list_with": {(*Compiler).btListWith, AltType(values.LIST)},
Expand All @@ -66,6 +68,7 @@ var BUILTINS = map[string]functionAndReturnType{
"make_map": {(*Compiler).btMakeMap, AltType(values.MAP)},
"make_pair": {(*Compiler).btMakePair, AltType(values.PAIR)},
"make_set": {(*Compiler).btMakeSet, AltType(values.SET)},
"make_snippet": {(*Compiler).btMakeSnippet, AltType(values.SNIPPET)},
"map_with": {(*Compiler).btMapWith, AltType(values.MAP)},
"map_without": {(*Compiler).btMapWithout, AltType(values.MAP)},
"modulo_integers": {(*Compiler).btModuloIntegers, AltType(values.ERROR, values.INT)},
Expand All @@ -75,9 +78,7 @@ var BUILTINS = map[string]functionAndReturnType{
"multiply_integer_by_float": {(*Compiler).btMultiplyIntegerByFloat, AltType(values.FLOAT)},
"negate_float": {(*Compiler).btNegateFloat, AltType(values.FLOAT)},
"negate_integer": {(*Compiler).btNegateInteger, AltType(values.INT)},
"post_html": {(*Compiler).btPostSpecialSnippet, AltType(values.SUCCESSFUL_VALUE, values.ERROR)},
"post_to_output": {(*Compiler).btPostToOutput, AltType(values.SUCCESSFUL_VALUE)},
"post_sql": {(*Compiler).btPostSpecialSnippet, AltType(values.SUCCESSFUL_VALUE, values.ERROR)},
"post_to_terminal": {(*Compiler).btPostToTerminal, AltType(values.SUCCESSFUL_VALUE)},
"rune": {(*Compiler).btRune, AltType(values.RUNE)},
"string": {(*Compiler).btString, AltType(values.STRING)},
Expand Down Expand Up @@ -159,6 +160,10 @@ func (cp *Compiler) btCastToSet(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.Cast, dest, args[0], uint32(values.SET))
}

func (cp *Compiler) btCastToSnippet(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.Cast, dest, args[0], uint32(values.SNIPPET))
}

func (cp *Compiler) btCastToString(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.Cast, dest, args[0], uint32(values.STRING))
}
Expand Down Expand Up @@ -268,6 +273,10 @@ func (cp *Compiler) btLenSet(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.LenS, dest, args[0])
}

func (cp *Compiler) btLenSnippet(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.LnSn, dest, args[0])
}

func (cp *Compiler) btLenString(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.Lens, dest, args[0])
}
Expand Down Expand Up @@ -312,6 +321,10 @@ func (cp *Compiler) btMakePair(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.Mkpr, dest, args[0], args[2])
}

func (cp *Compiler) btMakeSnippet(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.CoSn, dest, args[0], cp.reserveToken(tok))
}

func (cp *Compiler) btMakeSet(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.Mkst, dest, args[0], cp.reserveToken(tok))
}
Expand Down Expand Up @@ -357,10 +370,6 @@ func (cp *Compiler) btPostToOutput(tok *token.Token, dest uint32, args []uint32)
cp.Emit(vm.Asgm, dest, values.C_OK)
}

func (cp *Compiler) btPostSpecialSnippet(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.Psnp, dest, args[0])
}

func (cp *Compiler) btPostToTerminal(tok *token.Token, dest uint32, args []uint32) {
cp.Emit(vm.Outt, args[0])
cp.Emit(vm.Asgm, dest, values.C_OK)
Expand Down
2 changes: 0 additions & 2 deletions source/compiler/compfcall.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,6 @@ func (cp *Compiler) generateBranch(b *bindle) AlternateType {
}

var TYPE_COMPARISONS = map[string]vm.Opcode{
"snippet": vm.Qspt,
"snippet?": vm.Qspq,
"any": vm.Qsng,
"any?": vm.Qsnq,
"struct": vm.Qstr,
Expand Down
111 changes: 32 additions & 79 deletions source/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"os"
"reflect"
"strconv"
"strings"
"testing"

"github.com/tim-hardcastle/Pipefish/source/ast"
Expand Down Expand Up @@ -81,8 +80,8 @@ type CommonCompilerBindle struct {
func NewCommonCompilerBindle() *CommonCompilerBindle {
newBindle := &CommonCompilerBindle{
SharedTypenameToTypeList: map[string]AlternateType{
"any": AltType(values.INT, values.BOOL, values.STRING, values.RUNE, values.TYPE, values.FUNC, values.PAIR, values.LIST, values.MAP, values.SET, values.LABEL),
"any?": AltType(values.NULL, values.INT, values.BOOL, values.STRING, values.RUNE, values.FLOAT, values.TYPE, values.FUNC, values.PAIR, values.LIST, values.MAP, values.SET, values.LABEL),
"any": AltType(values.INT, values.BOOL, values.STRING, values.RUNE, values.TYPE, values.FUNC, values.PAIR, values.LIST, values.MAP, values.SET, values.LABEL, values.SNIPPET),
"any?": AltType(values.NULL, values.INT, values.BOOL, values.STRING, values.RUNE, values.FLOAT, values.TYPE, values.FUNC, values.PAIR, values.LIST, values.MAP, values.SET, values.LABEL, values.SNIPPET),
},
AnyTypeScheme: AlternateType{},
AnyTuple: AlternateType{},
Expand Down Expand Up @@ -396,6 +395,7 @@ NodeTypeSwitch:
// Tuples, ditto.
// Strings, ditto.
// Pairs, by integers.
// Snippets, by integers.
// Names of enum types, by integers. Query, add slice too?
// Maps, by any value we can Compare with another value.
// Structs, by a label, preferably an appropriate one.
Expand Down Expand Up @@ -459,6 +459,17 @@ NodeTypeSwitch:
}
rtnTypes = cp.GetAlternateTypeFromTypeName("any?").Union(AltType(values.ERROR))
}
if containerType.isOnlyCloneOf(cp.Vm, values.SNIPPET) {
if indexType.isOnlyCloneOf(cp.Vm, values.INT) {
cp.put(vm.IxSn, container, index, errTok)
break
}
if indexType.cannotBeACloneOf(cp.Vm, values.INT) {
cp.P.Throw("comp/index/snippet", node.GetToken())
break
}
rtnTypes = cp.GetAlternateTypeFromTypeName("any?").Union(AltType(values.ERROR))
}
if containerType.isOnly(values.TYPE) {
if indexType.isOnlyCloneOf(cp.Vm, values.INT) {
cp.put(vm.Idxt, container, index, errTok)
Expand Down Expand Up @@ -912,6 +923,11 @@ NodeTypeSwitch:
cp.Reserve(values.RUNE, node.Value, node.GetToken())
rtnTypes, rtnConst = AltType(values.RUNE), true
break
case *ast.SnippetLiteral:
snF := cp.reserveSnippetFactory(env, node, ctxt)
cp.put(vm.MkSn, snF)
rtnTypes, rtnConst = AltType(values.SNIPPET), false
break
case *ast.StringLiteral:
cp.Reserve(values.STRING, node.Value, node.GetToken())
rtnTypes, rtnConst = AltType(values.STRING), true
Expand All @@ -920,18 +936,6 @@ NodeTypeSwitch:
panic("This is used only in the vmmaker and should never be compiled.")
case *ast.SuffixExpression:
resolvingCompiler := cp.getResolvingCompiler(node, node.Namespace, ac)
if node.GetToken().Type == token.EMDASH {
switch t := node.Args[0].(type) {
case *ast.TypeLiteral:
snF := cp.reserveSnippetFactory(t.Value, env, node, ctxt)
cp.put(vm.MkSn, snF)
rtnTypes, rtnConst = cp.TypeNameToTypeScheme[t.Value], false
break NodeTypeSwitch
default:
cp.P.Throw("comp/snippet/type", node.Args[0].GetToken()) // There is no reason why this should be a first-class value, that would just be confusing. Hence the error.
break NodeTypeSwitch
}
}
if node.GetToken().Type == token.DOTDOTDOT {
if len(node.Args) != 1 {
cp.P.Throw("comp/splat/args", node.GetToken())
Expand Down Expand Up @@ -1859,17 +1863,10 @@ func (cp *Compiler) getLambdaStart() uint32 {

// A function for making snippet factories.

func (cp *Compiler) reserveSnippetFactory(t string, env *Environment, fnNode *ast.SuffixExpression, ctxt Context) uint32 {
cp.Cm("Reserving snippet factory.", &fnNode.Token)
snF := &vm.SnippetFactory{SnippetType: cp.ConcreteTypeNow(t), SourceString: fnNode.Token.Literal}
csk := vm.VANILLA_SNIPPET
switch {
case t == "SQL":
csk = vm.SQL_SNIPPET
case t == "HTML":
csk = vm.HTML_SNIPPET
}
snF.Bindle = cp.compileSnippet(fnNode.GetToken(), env, csk, snF.SourceString, ctxt)
func (cp *Compiler) reserveSnippetFactory(env *Environment, node *ast.SnippetLiteral, ctxt Context) uint32 {
cp.Cm("Reserving snippet factory.", &node.Token)
snF := &vm.SnippetFactory{}
snF.Bindle = cp.compileSnippet(node.GetToken(), env, node.Token.Literal, ctxt)
cp.Vm.SnippetFactories = append(cp.Vm.SnippetFactories, snF)
return uint32(len(cp.Vm.SnippetFactories) - 1)
}
Expand Down Expand Up @@ -2155,79 +2152,35 @@ func (cp *Compiler) compileMappingOrFilter(lhsTypes AlternateType, lhsConst bool
return AltType(values.LIST), lhsConst && rhsConst
}

func (cp *Compiler) compileSnippet(tok *token.Token, newEnv *Environment, csk vm.CompiledSnippetKind, sText string, ctxt Context) *vm.SnippetBindle {
func (cp *Compiler) compileSnippet(tok *token.Token, newEnv *Environment, sText string, ctxt Context) *values.SnippetBindle {
cp.Cm("Compile snippet", tok)
bindle := vm.SnippetBindle{Csk: csk}
bindle := values.SnippetBindle{}
bits, ok := text.GetTextWithBarsAsList(sText)
if !ok {
cp.P.Throw("comp/snippet/form", tok)
return &bindle
}
var buf strings.Builder
bindle.CodeLoc = cp.CodeTop()
c := 0
if len(bits) > 0 && len(bits[0]) > 0 && bits[0][0] == '|' {
cp.Reserve(values.STRING, "", tok)
bindle.ValueLocs = append(bindle.ValueLocs, cp.That())
}
for _, bit := range bits {
if len(bit) > 0 && bit[0] == '|' {
node := cp.P.ParseLine(tok.Source, bit[1:len(bit)-1])
newContext := ctxt
newContext.Env = newEnv
types, cst := cp.CompileNode(node, newContext)
types, _ := cp.CompileNode(node, newContext)
val := cp.That()

// Special sauce for the SQL snippets.
if types.isOnly(values.TYPE) && cst && csk == vm.SQL_SNIPPET {
typeNumbers := cp.Vm.Mem[cp.That()].V.(values.AbstractType).Types
if len(typeNumbers) == 1 && cp.Vm.ConcreteTypeInfo[typeNumbers[0]].IsStruct() {
sig, ok := cp.Vm.GetSqlSig(typeNumbers[0])
if !ok {
cp.P.Throw("comp/snippet/sig", tok, cp.Vm.DescribeType(typeNumbers[0], vm.LITERAL))
}
buf.WriteString(sig)
continue // ... the for loop.
}
}
// If it's a tuple of fixed length, we can split it and inject the values separately.
// If it's of indeterminate length then we need to throw an error.
numberOfInjectionSites := 1 // Default, if the type is any.
if types.Contains(values.TUPLE) {
lengths := lengths(types)
if lengths.Contains(-1) || len(lengths) > 1 { // ... then we can't infer the length and must throw an error.
cp.P.Throw("comp/snippet/tuple", tok)
}
numberOfInjectionSites, _ = lengths.GetArbitraryElement()
for i := 0; i < numberOfInjectionSites; i++ {
cp.put(vm.IxTn, val, uint32(i))
bindle.ValueLocs = append(bindle.ValueLocs, cp.That())
}
} else { // We have a any element so we add it to the injectable values.
bindle.ValueLocs = append(bindle.ValueLocs, val)
}
sep := ""
for i := 0; i < numberOfInjectionSites; i++ {
buf.WriteString(sep)
switch csk {
case vm.SQL_SNIPPET:
buf.WriteString("$")
c++
buf.WriteString(strconv.Itoa(c)) // The injection sites in SQL go $1 , $2 , $3 ...
case vm.HTML_SNIPPET:
buf.WriteString("{{index .Data ")
buf.WriteString(strconv.Itoa(c)) // The injection sites in HTML go {{index .Data 0}} , {{index .Data 1}} ...
buf.WriteString("}}")
c++
case vm.VANILLA_SNIPPET:
buf.WriteString("%v") // We produce a Go format string.
}
sep = ", "
cp.P.Throw("comp/snippet/tuple", tok)
}
bindle.ValueLocs = append(bindle.ValueLocs, val)
} else {
cp.Reserve(values.STRING, bit, tok)
bindle.ValueLocs = append(bindle.ValueLocs, cp.That())
buf.WriteString(bit)
}
}
cp.Reserve(values.STRING, buf.String(), tok)
bindle.ObjectStringLoc = cp.That()
return &bindle
}

Expand Down
3 changes: 1 addition & 2 deletions source/compiler/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,7 @@ func TestClones(t *testing.T) {
}
func TestSnippet(t *testing.T) {
tests := []test_helper.TestItem{
{`makeSn 42`, `Foo with (text::"zort |x| troz", data::["zort ", 42, " troz"])`},
{`post HTML --- zort |2 + 2| troz`, `OK`},

}
test_helper.RunTest(t, "snippets_test.pf", tests, test_helper.TestValues)
}
Expand Down
9 changes: 0 additions & 9 deletions source/compiler/test-files/snippets_test.pf
Original file line number Diff line number Diff line change
@@ -1,9 +0,0 @@
newtype

Foo = snippet

def

makeSn(x int) :
Foo --- zort |x| troz

2 changes: 2 additions & 0 deletions source/compiler/typeschemes.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var INITIAL_TYPE_SCHEMES = map[string]AlternateType{
"map": AltType(values.MAP),
"set": AltType(values.SET),
"label": AltType(values.LABEL),
"snippet": AltType(values.SNIPPET),
"func": AltType(values.FUNC),
"int?": AltType(values.NULL, values.INT),
"string?": AltType(values.NULL, values.STRING),
Expand All @@ -53,6 +54,7 @@ var INITIAL_TYPE_SCHEMES = map[string]AlternateType{
"set?": AltType(values.NULL, values.SET),
"label?": AltType(values.NULL, values.LABEL),
"func?": AltType(values.NULL, values.FUNC),
"snippet?": AltType(values.NULL, values.SNIPPET),
"null": AltType(values.NULL),
}

Expand Down
20 changes: 19 additions & 1 deletion source/err/errorfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,16 @@ var ErrorCreatorMap = map[string]ErrorCreator{
return "pair index is not of type" + emph("int")
},
Explanation: func(errors Errors, pos int, tok *token.Token, args ...any) string {
return "A pair may be indexed only by the integers " + emph("0") + " and " + emph("1")
return "A pair may be indexed only by the integers " + emph("0") + " and " + emph("1")
},
},

"comp/index/snippet": {
Message: func(tok *token.Token, args ...any) string {
return "snippet index is not of type" + emph("int")
},
Explanation: func(errors Errors, pos int, tok *token.Token, args ...any) string {
return "A snippet may be indexed only by integers."
},
},

Expand Down Expand Up @@ -2958,6 +2967,15 @@ var ErrorCreatorMap = map[string]ErrorCreator{
},
},

"vm/index/s": {
Message: func(tok *token.Token, args ...any) string {
return fmt.Sprintf("index %v of snippet is out of range", emph(args[0]))
},
Explanation: func(errors Errors, pos int, tok *token.Token, args ...any) string {
return fmt.Sprintf("The greatest value the index of a snippet can have is the length of the thing being indexed.")
},
},

"vm/label/exist": {
Message: func(tok *token.Token, args ...any) string {
return fmt.Sprintf("can't convert string %v to a label", emphStr(args[0]))
Expand Down
2 changes: 1 addition & 1 deletion source/hub/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

func StartHub(hub *Hub, in io.Reader, out io.Writer) {
var rline *readline.Instance
colonOrEmdash, _ := regexp.Compile(`.*[\w\s]*(:|---)[\s]*$`)
colonOrEmdash, _ := regexp.Compile(`.*[\w\s]*(:|--)[\s]*$`)
for {

// The hub's CurrentForm setting allows it to ask for information from the user instead of
Expand Down
1 change: 1 addition & 0 deletions source/initializer/gogen.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ var goTypes = map[string]string{
"map": "map[any]any",
"pair": "[2]any",
"set": "map[any]struct{}",
"snippet": "!",
"tuple": "[]any",
"type": "!",
}
Expand Down
Loading

0 comments on commit 340382b

Please sign in to comment.