<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/subex
diff options
context:
space:
mode:
Diffstat (limited to 'subex')
-rw-r--r--subex/parse.go128
-rw-r--r--subex/subexast.go9
-rw-r--r--subex/subexstate.go43
3 files changed, 105 insertions, 75 deletions
diff --git a/subex/parse.go b/subex/parse.go
index 6c19df4..2392b22 100644
--- a/subex/parse.go
+++ b/subex/parse.go
@@ -27,54 +27,47 @@ func isNumericRune(r rune) bool {
}
// Having just parsed a `, read until the next ` and parse the contents into a list of non-string atoms
-func parseNonStringLiteral(l RuneReader) (literals []walk.Scalar) {
- for {
- r := l.Next()
- if isNumericRune(r) {
- var builder strings.Builder
+func parseScalarLiteral(l RuneReader) (walk.Scalar, bool) {
+ r := l.Next()
+ if isNumericRune(r) {
+ var builder strings.Builder
+ builder.WriteRune(r)
+ for {
+ r := l.Next()
+ if !isNumericRune(r) {
+ l.Rewind()
+ break
+ }
builder.WriteRune(r)
- for {
- r := l.Next()
- if !isNumericRune(r) {
- l.Rewind()
- break
- }
- builder.WriteRune(r)
+ }
+ numberString := builder.String()
+ number, err := strconv.ParseFloat(numberString, 64)
+ if err != nil {
+ panic("Invalid number literal")
+ }
+ return walk.NumberScalar(number), true
+ }
+ switch r {
+ case 'n':
+ if accept(l, "u") && accept(l, "l") && accept(l, "l") {
+ return walk.NullScalar{}, true
+ } else {
+ panic("Invalid literal")
}
- numberString := builder.String()
- number, err := strconv.ParseFloat(numberString, 64)
- if err != nil {
- panic("Invalid number literal")
+ case 't':
+ if accept(l, "r") && accept(l, "u") && accept(l, "e") {
+ return walk.BoolScalar(true), true
+ } else {
+ panic("Invalid literal")
}
- literals = append(literals, walk.NumberScalar(number))
- continue
- }
- switch r {
- case '`', '~':
- return literals
- case ' ', '\t':
- continue
- case 'n':
- if accept(l, "u") && accept(l, "l") && accept(l, "l") {
- literals = append(literals, walk.NullScalar{})
- } else {
- panic("Invalid literal")
- }
- case 't':
- if accept(l, "r") && accept(l, "u") && accept(l, "e") {
- literals = append(literals, walk.BoolScalar(true))
- } else {
- panic("Invalid literal")
- }
- case 'f':
- if accept(l, "a") && accept(l, "l") && accept(l, "s") && accept(l, "e") {
- literals = append(literals, walk.BoolScalar(false))
- } else {
- panic("Invalid literal")
- }
- default:
+ case 'f':
+ if accept(l, "a") && accept(l, "l") && accept(l, "s") && accept(l, "e") {
+ return walk.BoolScalar(false), true
+ } else {
panic("Invalid literal")
- }
+ }
+ default:
+ panic("Invalid literal")
}
}
@@ -140,14 +133,16 @@ func parseRepeatRange(l RuneReader) (output []ConvexRange) {
return output
}
-func parseReplacement(l RuneReader) (output []OutputContentAST) {
+// TODO: Consider if it's worth making better use of the go type system to enforce output being all runes or all values
+func parseReplacement(l RuneReader, runic bool) (output []OutputContentAST) {
// TODO escaping
+ // TODO add arrays, maps and strings
loop: for {
r := l.Next()
switch r {
case eof:
- panic("Missing closing \"")
- case '=', '^', ':':
+ panic("Missing closing `")
+ case '`':
break loop
case '$':
slot := l.Next()
@@ -155,14 +150,17 @@ func parseReplacement(l RuneReader) (output []OutputContentAST) {
panic("Missing slot character")
}
output = append(output, OutputLoadAST{slot: slot})
- case '`':
- literals := parseNonStringLiteral(l)
- for _, literal := range literals {
- output = append(output, OutputValueLiteralAST {literal})
- }
default:
- panic("Invalid value to insert")
- //output = append(output, OutputValueLiteralAST{atom: walk.NewAtomStringRune(r)})
+ if runic {
+ output = append(output, OutputRuneLiteralAST {walk.StringRuneAtom(r)})
+ } else {
+ l.Rewind()
+ scalar, ok := parseScalarLiteral(l)
+ if !ok {
+ panic("Invalid scalar literal")
+ }
+ output = append(output, OutputValueLiteralAST {scalar})
+ }
}
}
return output
@@ -265,15 +263,9 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST {
case ')', ']', '"', '|', ';', '{', '+', '-', '*', '/', '!', '$':
l.Rewind()
return SubexASTEmpty{}
- case '=':
- replacement := parseReplacement(l)
- lhs = SubexASTOutput{replacement}
- case '`':
- literals := parseNonStringLiteral(l)
- lhs = SubexASTEmpty{}
- for _, literal := range literals {
- lhs = SubexASTConcat {lhs, SubexASTCopyScalar {literal}}
- }
+ // case '=':
+ // replacement := parseReplacement(l)
+ // lhs = SubexASTOutput{replacement}
// case '^':
// replacement := parseReplacement(l)
// replacement = append(
@@ -307,6 +299,8 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST {
panic("Missing matching ]")
}
}
+ case '`':
+ lhs = SubexASTOutput {parseReplacement(l, runic)}
case '~':
if runic {
lhs = SubexASTCopyRune {'~'}
@@ -339,8 +333,12 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST {
if runic {
lhs = SubexASTCopyRune {r}
} else {
- // TODO: Allow whitespace outside of runic sections
- panic("Tried to match rune outside of string")
+ l.Rewind()
+ scalar, ok := parseScalarLiteral(l)
+ if !ok {
+ panic("Invalid subex")
+ }
+ lhs = SubexASTCopyScalar {scalar}
}
}
loop: for {
diff --git a/subex/subexast.go b/subex/subexast.go
index 31c77ba..b1ac931 100644
--- a/subex/subexast.go
+++ b/subex/subexast.go
@@ -246,7 +246,14 @@ type OutputValueLiteralAST struct {
atom walk.Value
}
func (ast OutputValueLiteralAST) compile(slotMap *SlotMap) OutputContent {
- return OutputAtomLiteral {ast.atom}
+ return OutputValueLiteral {ast.atom}
+}
+
+type OutputRuneLiteralAST struct {
+ rune walk.StringRuneAtom
+}
+func (ast OutputRuneLiteralAST) compile(slotMap *SlotMap) OutputContent {
+ return OutputRuneLiteral {ast.rune}
}
// Output a series of Atoms without reading anything from input
diff --git a/subex/subexstate.go b/subex/subexstate.go
index 7ffd592..0b21c93 100644
--- a/subex/subexstate.go
+++ b/subex/subexstate.go
@@ -121,29 +121,54 @@ func (state SubexStoreEndState) accepting(aux auxiliaryState) []OutputStack {
// A part of an output literal, either an Atom or a slot from which to load
type OutputContent interface {
- // Given the current store, return the []Atom produced by the TransducerOutput
- build(Store) walk.ValueList
+ // Given the current store, return the ValueList produced by the TransducerOutput
+ buildValues(Store) walk.ValueList
+ // Given the current store, return the RuneList produced by the TransducerOutput
+ buildRunes(Store) walk.RuneList
}
-// An OutputContent which is just an Atom literal
-type OutputAtomLiteral struct {
- atom walk.Value
+// An OutputContent which is just a Value literal
+type OutputValueLiteral struct {
+ value walk.Value
}
-func (replacement OutputAtomLiteral) build(store Store) walk.ValueList {
- return walk.ValueList{replacement.atom}
+func (replacement OutputValueLiteral) buildValues(store Store) walk.ValueList {
+ return walk.ValueList{replacement.value}
+}
+func (replacement OutputValueLiteral) buildRunes(store Store) walk.RuneList {
+ // TODO: serialise to JSON
+ panic("Unimplemented!")
+}
+
+// An OutputContent which is just a rune literal
+type OutputRuneLiteral struct {
+ rune walk.StringRuneAtom
+}
+func (replacement OutputRuneLiteral) buildValues(store Store) walk.ValueList {
+ // TODO: Try to deserialise
+ panic("Unimplemented!")
+}
+func (replacement OutputRuneLiteral) buildRunes(store Store) walk.RuneList {
+ return walk.RuneList {replacement.rune}
}
// An OutputContent which is a slot that is loaded from
type OutputLoad struct {
slot int
}
-func (replacement OutputLoad) build(store Store) walk.ValueList {
+func (replacement OutputLoad) buildValues(store Store) walk.ValueList {
values, isValues := store[replacement.slot].(walk.ValueList)
if !isValues {
panic("Tried to output non-values list")
}
return values
}
+func (replacement OutputLoad) buildRunes(store Store) walk.RuneList {
+ runes, isRunes := store[replacement.slot].(walk.RuneList)
+ if !isRunes {
+ panic("Tried to output non-runes as runes")
+ }
+ return runes
+}
// Don't read in anything, just output the series of data and slots specified
type SubexOutputState struct {
@@ -155,7 +180,7 @@ type SubexOutputState struct {
func (state SubexOutputState) build(store Store) walk.ValueList {
var result walk.ValueList
for _, part := range state.content {
- result = append(result, part.build(store)...)
+ result = append(result, part.buildValues(store)...)
}
return result
}