Changed ParseDataText function return values

This commit is contained in:
Alexei Anoshenko 2025-06-25 17:42:32 +03:00
parent 3090a0e94f
commit c3c8b9e858
13 changed files with 609 additions and 545 deletions

View File

@ -2,6 +2,7 @@
* Added support of binding * Added support of binding
* Added "binding" argument to CreateViewFromResources, CreateViewFromText, and CreateViewFromObject functions * Added "binding" argument to CreateViewFromResources, CreateViewFromText, and CreateViewFromObject functions
* Changed ParseDataText function return values
# v0.19.0 # v0.19.0

View File

@ -164,64 +164,68 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
DebugLog(message) DebugLog(message)
} }
if obj := ParseDataText(message); obj != nil { obj, err := ParseDataText(message)
var session Session = nil if err != nil {
var response chan string = nil ErrorLog(err.Error())
return
}
if cookie, err := req.Cookie("session"); err == nil { var session Session = nil
sessionID, err := strconv.Atoi(cookie.Value) var response chan string = nil
if err != nil {
ErrorLog(err.Error()) if cookie, err := req.Cookie("session"); err == nil {
} else if info, ok := app.sessions[sessionID]; ok && info.response != nil { sessionID, err := strconv.Atoi(cookie.Value)
response = info.response if err != nil {
session = info.session ErrorLog(err.Error())
} } else if info, ok := app.sessions[sessionID]; ok && info.response != nil {
} response = info.response
session = info.session
command := obj.Tag() }
startSession := false }
if session == nil || command == "startSession" { command := obj.Tag()
events := make(chan DataObject, 1024) startSession := false
bridge := createHttpBridge(req)
response = bridge.response if session == nil || command == "startSession" {
answer := "" events := make(chan DataObject, 1024)
session, answer = app.startSession(obj, events, bridge, response) bridge := createHttpBridge(req)
response = bridge.response
bridge.writeMessage(answer) answer := ""
session.onStart() session, answer = app.startSession(obj, events, bridge, response)
if command == "session-resume" {
session.onResume() bridge.writeMessage(answer)
} session.onStart()
bridge.sendResponse() if command == "session-resume" {
session.onResume()
setSessionIDCookie(w, session.ID()) }
startSession = true bridge.sendResponse()
go sessionEventHandler(session, events, bridge) setSessionIDCookie(w, session.ID())
} startSession = true
if !startSession { go sessionEventHandler(session, events, bridge)
switch command { }
case "nop":
session.sendResponse() if !startSession {
switch command {
case "session-close": case "nop":
session.onFinish() session.sendResponse()
session.App().removeSession(session.ID())
return case "session-close":
session.onFinish()
default: session.App().removeSession(session.ID())
if !session.handleAnswer(command, obj) { return
session.addToEventsQueue(obj)
} default:
if !session.handleAnswer(command, obj) {
session.addToEventsQueue(obj)
} }
} }
}
io.WriteString(w, <-response)
for len(response) > 0 {
io.WriteString(w, <-response) io.WriteString(w, <-response)
for len(response) > 0 {
io.WriteString(w, <-response)
}
} }
} }
} }
@ -241,58 +245,61 @@ func (app *application) socketReader(bridge *wsBridge) {
DebugLog("🖥️ -> " + message) DebugLog("🖥️ -> " + message)
} }
if obj := ParseDataText(message); obj != nil { obj, err := ParseDataText(message)
command := obj.Tag() if err != nil {
switch command { ErrorLog(err.Error())
case "startSession": return
answer := "" }
switch command := obj.Tag(); command {
case "startSession":
answer := ""
if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
if !bridge.writeMessage(answer) {
return
}
session.onStart()
go sessionEventHandler(session, events, bridge)
}
case "reconnect":
session = nil
if sessionText, ok := obj.PropertyValue("session"); ok {
if sessionID, err := strconv.Atoi(sessionText); err == nil {
if info, ok := app.sessions[sessionID]; ok {
session = info.session
session.setBridge(events, bridge)
go sessionEventHandler(session, events, bridge)
session.onReconnect()
} else {
DebugLogF("Session #%d not exists", sessionID)
}
} else {
ErrorLog(`strconv.Atoi(sessionText) error: ` + err.Error())
}
} else {
ErrorLog(`"session" key not found`)
}
if session == nil {
/* answer := ""
if session, answer = app.startSession(obj, events, bridge, nil); session != nil { if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
if !bridge.writeMessage(answer) { if !bridge.writeMessage(answer) {
return return
} }
session.onStart() session.onStart()
go sessionEventHandler(session, events, bridge) go sessionEventHandler(session, events, bridge)
bridge.writeMessage("restartSession();")
} }
*/
bridge.writeMessage("reloadPage();")
return
}
case "reconnect": default:
session = nil if !session.handleAnswer(command, obj) {
if sessionText, ok := obj.PropertyValue("session"); ok { events <- obj
if sessionID, err := strconv.Atoi(sessionText); err == nil {
if info, ok := app.sessions[sessionID]; ok {
session = info.session
session.setBridge(events, bridge)
go sessionEventHandler(session, events, bridge)
session.onReconnect()
} else {
DebugLogF("Session #%d not exists", sessionID)
}
} else {
ErrorLog(`strconv.Atoi(sessionText) error: ` + err.Error())
}
} else {
ErrorLog(`"session" key not found`)
}
if session == nil {
/* answer := ""
if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
if !bridge.writeMessage(answer) {
return
}
session.onStart()
go sessionEventHandler(session, events, bridge)
bridge.writeMessage("restartSession();")
}
*/
bridge.writeMessage("reloadPage();")
return
}
default:
if !session.handleAnswer(command, obj) {
events <- obj
}
} }
} }
} }

View File

@ -78,6 +78,16 @@ func createBackground(obj DataObject) BackgroundElement {
return result return result
} }
func parseBackgroundText(text string) BackgroundElement {
obj, err := ParseDataText(text)
if err != nil {
ErrorLog(err.Error())
return nil
}
return createBackground(obj)
}
func parseBackgroundValue(value any) []BackgroundElement { func parseBackgroundValue(value any) []BackgroundElement {
switch value := value.(type) { switch value := value.(type) {
@ -96,12 +106,8 @@ func parseBackgroundValue(value any) []BackgroundElement {
} else { } else {
return nil return nil
} }
} else if obj := ParseDataText(el.Value()); obj != nil { } else if element := parseBackgroundText(el.Value()); element != nil {
if element := createBackground(obj); element != nil { background = append(background, element)
background = append(background, element)
} else {
return nil
}
} else { } else {
return nil return nil
} }
@ -125,21 +131,15 @@ func parseBackgroundValue(value any) []BackgroundElement {
return background return background
case string: case string:
if obj := ParseDataText(value); obj != nil { if element := parseBackgroundText(value); element != nil {
if element := createBackground(obj); element != nil { return []BackgroundElement{element}
return []BackgroundElement{element}
}
} }
case []string: case []string:
elements := make([]BackgroundElement, 0, len(value)) elements := make([]BackgroundElement, 0, len(value))
for _, element := range value { for _, text := range value {
if obj := ParseDataText(element); obj != nil { if element := parseBackgroundText(text); element != nil {
if element := createBackground(obj); element != nil { elements = append(elements, element)
elements = append(elements, element)
} else {
return nil
}
} else { } else {
return nil return nil
} }
@ -148,18 +148,14 @@ func parseBackgroundValue(value any) []BackgroundElement {
case []any: case []any:
elements := make([]BackgroundElement, 0, len(value)) elements := make([]BackgroundElement, 0, len(value))
for _, element := range value { for _, val := range value {
switch element := element.(type) { switch val := val.(type) {
case BackgroundElement: case BackgroundElement:
elements = append(elements, element) elements = append(elements, val)
case string: case string:
if obj := ParseDataText(element); obj != nil { if element := parseBackgroundText(val); element != nil {
if element := createBackground(obj); element != nil { elements = append(elements, element)
elements = append(elements, element)
} else {
return nil
}
} else { } else {
return nil return nil
} }

View File

@ -610,7 +610,10 @@ func borderSet(properties Properties, tag PropertyName, value any) []PropertyNam
case Left, Right, Top, Bottom: case Left, Right, Top, Bottom:
switch value := value.(type) { switch value := value.(type) {
case string: case string:
if obj := ParseDataText(value); obj != nil { obj, err := ParseDataText(value)
if err != nil {
ErrorLog(err.Error())
} else {
return setSingleBorderObject(tag, obj) return setSingleBorderObject(tag, obj)
} }

805
data.go
View File

@ -1,6 +1,8 @@
package rui package rui
import ( import (
"errors"
"fmt"
"slices" "slices"
"strings" "strings"
"unicode" "unicode"
@ -48,6 +50,9 @@ type DataObject interface {
// ToParams create a params(map) representation of a data object // ToParams create a params(map) representation of a data object
ToParams() Params ToParams() Params
// PropertyByTag removes a data node corresponding to a property tag and returns it
RemovePropertyByTag(tag string) DataNode
} }
// DataNodeType defines the type of DataNode // DataNodeType defines the type of DataNode
@ -162,6 +167,28 @@ func (object *dataObject) PropertyByTag(tag string) DataNode {
return nil return nil
} }
func (object *dataObject) RemovePropertyByTag(tag string) DataNode {
if object.property != nil {
for i, node := range object.property {
if node.Tag() == tag {
switch i {
case 0:
object.property = object.property[1:]
case len(object.property) - 1:
object.property = object.property[:len(object.property)-1]
default:
object.property = append(object.property[:i], object.property[i+1:]...)
}
return node
}
}
}
return nil
}
func (object *dataObject) PropertyValue(tag string) (string, bool) { func (object *dataObject) PropertyValue(tag string) (string, bool) {
if node := object.PropertyByTag(tag); node != nil && node.Type() == TextNode { if node := object.PropertyByTag(tag); node != nil && node.Type() == TextNode {
return node.Text(), true return node.Text(), true
@ -318,404 +345,402 @@ func (node *dataNode) ArrayAsParams() []Params {
return result return result
} }
type dataParser struct {
data []rune
size int
pos int
line int
lineStart int
}
func (parser *dataParser) skipSpaces(skipNewLine bool) {
for parser.pos < parser.size {
switch parser.data[parser.pos] {
case '\n':
if !skipNewLine {
return
}
parser.line++
parser.lineStart = parser.pos + 1
case '/':
if parser.pos+1 < parser.size {
switch parser.data[parser.pos+1] {
case '/':
parser.pos += 2
for parser.pos < parser.size && parser.data[parser.pos] != '\n' {
parser.pos++
}
parser.pos--
case '*':
parser.pos += 3
for {
if parser.pos >= parser.size {
ErrorLog("Unexpected end of file")
return
}
if parser.data[parser.pos-1] == '*' && parser.data[parser.pos] == '/' {
break
}
if parser.data[parser.pos-1] == '\n' {
parser.line++
parser.lineStart = parser.pos
}
parser.pos++
}
default:
return
}
}
case ' ', '\t':
// do nothing
default:
if !unicode.IsSpace(parser.data[parser.pos]) {
return
}
}
parser.pos++
}
}
func (parser *dataParser) parseTag() (string, error) {
parser.skipSpaces(true)
startPos := parser.pos
switch parser.data[parser.pos] {
case '`':
parser.pos++
startPos++
for parser.data[parser.pos] != '`' {
parser.pos++
if parser.pos >= parser.size {
return string(parser.data[startPos:parser.size]), errors.New("unexpected end of text")
}
}
str := string(parser.data[startPos:parser.pos])
parser.pos++
return str, nil
case '\'', '"':
stopSymbol := parser.data[parser.pos]
parser.pos++
startPos++
slash := false
for stopSymbol != parser.data[parser.pos] {
if parser.data[parser.pos] == '\\' {
parser.pos += 2
slash = true
} else {
parser.pos++
}
if parser.pos >= parser.size {
return string(parser.data[startPos:parser.size]), errors.New("unexpected end of text")
}
}
if !slash {
str := string(parser.data[startPos:parser.pos])
parser.pos++
parser.skipSpaces(false)
return str, nil
}
buffer := make([]rune, parser.pos-startPos+1)
n1 := 0
n2 := startPos
invalidEscape := func() (string, error) {
str := string(parser.data[startPos:parser.pos])
parser.pos++
return str, fmt.Errorf(`invalid escape sequence in "%s" (position %d)`, str, n2-2-startPos)
}
for n2 < parser.pos {
if parser.data[n2] != '\\' {
buffer[n1] = parser.data[n2]
n2++
} else {
n2 += 2
switch parser.data[n2-1] {
case 'n':
buffer[n1] = '\n'
case 'r':
buffer[n1] = '\r'
case 't':
buffer[n1] = '\t'
case '"':
buffer[n1] = '"'
case '\'':
buffer[n1] = '\''
case '\\':
buffer[n1] = '\\'
case 'x', 'X':
if n2+2 > parser.pos {
return invalidEscape()
}
x := 0
for range 2 {
ch := parser.data[n2]
if ch >= '0' && ch <= '9' {
x = x*16 + int(ch-'0')
} else if ch >= 'a' && ch <= 'f' {
x = x*16 + int(ch-'a'+10)
} else if ch >= 'A' && ch <= 'F' {
x = x*16 + int(ch-'A'+10)
} else {
return invalidEscape()
}
n2++
}
buffer[n1] = rune(x)
case 'u', 'U':
if n2+4 > parser.pos {
return invalidEscape()
}
x := 0
for range 4 {
ch := parser.data[n2]
if ch >= '0' && ch <= '9' {
x = x*16 + int(ch-'0')
} else if ch >= 'a' && ch <= 'f' {
x = x*16 + int(ch-'a'+10)
} else if ch >= 'A' && ch <= 'F' {
x = x*16 + int(ch-'A'+10)
} else {
return invalidEscape()
}
n2++
}
buffer[n1] = rune(x)
default:
str := string(parser.data[startPos:parser.pos])
return str, fmt.Errorf(`invalid escape sequence in "%s" (position %d)`, str, n2-2-startPos)
}
}
n1++
}
parser.pos++
parser.skipSpaces(false)
return string(buffer[0:n1]), nil
}
for parser.pos < parser.size && !parser.stopSymbol(parser.data[parser.pos]) {
parser.pos++
}
endPos := parser.pos
parser.skipSpaces(false)
if startPos == endPos {
//ErrorLog("empty tag")
return "", nil
}
return string(parser.data[startPos:endPos]), nil
}
func (parser *dataParser) stopSymbol(symbol rune) bool {
return unicode.IsSpace(symbol) ||
slices.Contains([]rune{'=', '{', '}', '[', ']', ',', ' ', '\t', '\n', '\'', '"', '`', '/'}, symbol)
}
func (parser *dataParser) parseNode() (DataNode, error) {
var tag string
var err error
if tag, err = parser.parseTag(); err != nil {
return nil, err
}
parser.skipSpaces(true)
if parser.data[parser.pos] != '=' {
return nil, fmt.Errorf("expected '=' after a tag name (line: %d, position: %d)", parser.line, parser.pos-parser.lineStart)
}
parser.pos++
parser.skipSpaces(true)
switch parser.data[parser.pos] {
case '[':
node := new(dataNode)
node.tag = tag
if node.array, err = parser.parseArray(); err != nil {
return nil, err
}
return node, nil
case '{':
node := new(dataNode)
node.tag = tag
if node.value, err = parser.parseObject("_"); err != nil {
return nil, err
}
return node, nil
case '}', ']', '=':
return nil, fmt.Errorf(`expected '[', '{' or a tag name after '=' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
default:
var str string
if str, err = parser.parseTag(); err != nil {
return nil, err
}
node := new(dataNode)
node.tag = tag
if parser.data[parser.pos] == '{' {
if node.value, err = parser.parseObject(str); err != nil {
return nil, err
}
} else {
val := new(dataStringValue)
val.value = str
node.value = val
}
return node, nil
}
}
func (parser *dataParser) parseObject(tag string) (DataObject, error) {
if parser.data[parser.pos] != '{' {
return nil, fmt.Errorf(`expected '{' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
}
parser.pos++
obj := new(dataObject)
obj.tag = tag
obj.property = []DataNode{}
for parser.pos < parser.size {
parser.skipSpaces(true)
if parser.data[parser.pos] == '}' {
parser.pos++
parser.skipSpaces(false)
return obj, nil
}
node, err := parser.parseNode()
if err != nil {
return nil, err
}
obj.property = append(obj.property, node)
if parser.data[parser.pos] == '}' {
parser.pos++
parser.skipSpaces(true)
return obj, nil
} else if parser.data[parser.pos] != ',' && parser.data[parser.pos] != '\n' {
return nil, fmt.Errorf(`expected '}', '\n' or ',' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
}
if parser.data[parser.pos] != '\n' {
parser.pos++
}
parser.skipSpaces(true)
for parser.data[parser.pos] == ',' {
parser.pos++
parser.skipSpaces(true)
}
}
return nil, errors.New("unexpected end of text")
}
func (parser *dataParser) parseArray() ([]DataValue, error) {
parser.pos++
parser.skipSpaces(true)
array := []DataValue{}
for parser.pos < parser.size {
parser.skipSpaces(true)
for parser.data[parser.pos] == ',' && parser.pos < parser.size {
parser.pos++
parser.skipSpaces(true)
}
if parser.pos >= parser.size {
break
}
if parser.data[parser.pos] == ']' {
parser.pos++
parser.skipSpaces(true)
return array, nil
}
tag, err := parser.parseTag()
if err != nil {
return nil, err
}
if parser.data[parser.pos] == '{' {
obj, err := parser.parseObject(tag)
if err != nil {
return nil, err
}
array = append(array, obj)
} else {
val := new(dataStringValue)
val.value = tag
array = append(array, val)
}
switch parser.data[parser.pos] {
case ']', ',', '\n':
default:
return nil, fmt.Errorf(`expected ']' or ',' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
}
/*
if data[pos] == ']' {
pos++
skipSpaces()
return array, nil
} else if data[pos] != ',' {
return nil, fmt.Errorf("Expected ']' or ',' (line: %d, position: %d)", line, pos-lineStart)
}
pos++
skipSpaces()
*/
}
return nil, errors.New("unexpected end of text")
}
// ParseDataText - parse text and return DataNode // ParseDataText - parse text and return DataNode
func ParseDataText(text string) DataObject { func ParseDataText(text string) (DataObject, error) {
if strings.ContainsAny(text, "\r") { if strings.ContainsAny(text, "\r") {
text = strings.ReplaceAll(text, "\r\n", "\n") text = strings.ReplaceAll(text, "\r\n", "\n")
text = strings.ReplaceAll(text, "\r", "\n") text = strings.ReplaceAll(text, "\r", "\n")
} }
data := append([]rune(text), rune(0))
pos := 0
size := len(data) - 1
line := 1
lineStart := 0
skipSpaces := func(skipNewLine bool) { parser := dataParser{
for pos < size { data: append([]rune(text), rune(0)),
switch data[pos] { pos: 0,
case '\n': line: 1,
if !skipNewLine { lineStart: 0,
return
}
line++
lineStart = pos + 1
case '/':
if pos+1 < size {
switch data[pos+1] {
case '/':
pos += 2
for pos < size && data[pos] != '\n' {
pos++
}
pos--
case '*':
pos += 3
for {
if pos >= size {
ErrorLog("Unexpected end of file")
return
}
if data[pos-1] == '*' && data[pos] == '/' {
break
}
if data[pos-1] == '\n' {
line++
lineStart = pos
}
pos++
}
default:
return
}
}
case ' ', '\t':
// do nothing
default:
if !unicode.IsSpace(data[pos]) {
return
}
}
pos++
}
} }
parser.size = len(parser.data) - 1
parseTag := func() (string, bool) { tag, err := parser.parseTag()
skipSpaces(true) if err != nil {
startPos := pos return nil, err
switch data[pos] {
case '`':
pos++
startPos++
for data[pos] != '`' {
pos++
if pos >= size {
ErrorLog("Unexpected end of text")
return string(data[startPos:size]), false
}
}
str := string(data[startPos:pos])
pos++
return str, true
case '\'', '"':
stopSymbol := data[pos]
pos++
startPos++
slash := false
for stopSymbol != data[pos] {
if data[pos] == '\\' {
pos += 2
slash = true
} else {
pos++
}
if pos >= size {
ErrorLog("Unexpected end of text")
return string(data[startPos:size]), false
}
}
if !slash {
str := string(data[startPos:pos])
pos++
skipSpaces(false)
return str, true
}
buffer := make([]rune, pos-startPos+1)
n1 := 0
n2 := startPos
invalidEscape := func() (string, bool) {
str := string(data[startPos:pos])
pos++
ErrorLogF("Invalid escape sequence in \"%s\" (position %d)", str, n2-2-startPos)
return str, false
}
for n2 < pos {
if data[n2] != '\\' {
buffer[n1] = data[n2]
n2++
} else {
n2 += 2
switch data[n2-1] {
case 'n':
buffer[n1] = '\n'
case 'r':
buffer[n1] = '\r'
case 't':
buffer[n1] = '\t'
case '"':
buffer[n1] = '"'
case '\'':
buffer[n1] = '\''
case '\\':
buffer[n1] = '\\'
case 'x', 'X':
if n2+2 > pos {
return invalidEscape()
}
x := 0
for range 2 {
ch := data[n2]
if ch >= '0' && ch <= '9' {
x = x*16 + int(ch-'0')
} else if ch >= 'a' && ch <= 'f' {
x = x*16 + int(ch-'a'+10)
} else if ch >= 'A' && ch <= 'F' {
x = x*16 + int(ch-'A'+10)
} else {
return invalidEscape()
}
n2++
}
buffer[n1] = rune(x)
case 'u', 'U':
if n2+4 > pos {
return invalidEscape()
}
x := 0
for range 4 {
ch := data[n2]
if ch >= '0' && ch <= '9' {
x = x*16 + int(ch-'0')
} else if ch >= 'a' && ch <= 'f' {
x = x*16 + int(ch-'a'+10)
} else if ch >= 'A' && ch <= 'F' {
x = x*16 + int(ch-'A'+10)
} else {
return invalidEscape()
}
n2++
}
buffer[n1] = rune(x)
default:
str := string(data[startPos:pos])
ErrorLogF("Invalid escape sequence in \"%s\" (position %d)", str, n2-2-startPos)
return str, false
}
}
n1++
}
pos++
skipSpaces(false)
return string(buffer[0:n1]), true
}
stopSymbol := func(symbol rune) bool {
return unicode.IsSpace(symbol) ||
slices.Contains([]rune{'=', '{', '}', '[', ']', ',', ' ', '\t', '\n', '\'', '"', '`', '/'}, symbol)
}
for pos < size && !stopSymbol(data[pos]) {
pos++
}
endPos := pos
skipSpaces(false)
if startPos == endPos {
//ErrorLog("empty tag")
return "", true
}
return string(data[startPos:endPos]), true
} }
return parser.parseObject(tag)
var parseObject func(tag string) DataObject
var parseArray func() []DataValue
parseNode := func() DataNode {
var tag string
var ok bool
if tag, ok = parseTag(); !ok {
return nil
}
skipSpaces(true)
if data[pos] != '=' {
ErrorLogF("expected '=' after a tag name (line: %d, position: %d)", line, pos-lineStart)
return nil
}
pos++
skipSpaces(true)
switch data[pos] {
case '[':
node := new(dataNode)
node.tag = tag
if node.array = parseArray(); node.array == nil {
return nil
}
return node
case '{':
node := new(dataNode)
node.tag = tag
if node.value = parseObject("_"); node.value == nil {
return nil
}
return node
case '}', ']', '=':
ErrorLogF("Expected '[', '{' or a tag name after '=' (line: %d, position: %d)", line, pos-lineStart)
return nil
default:
var str string
if str, ok = parseTag(); !ok {
return nil
}
node := new(dataNode)
node.tag = tag
if data[pos] == '{' {
if node.value = parseObject(str); node.value == nil {
return nil
}
} else {
val := new(dataStringValue)
val.value = str
node.value = val
}
return node
}
}
parseObject = func(tag string) DataObject {
if data[pos] != '{' {
ErrorLogF("Expected '{' (line: %d, position: %d)", line, pos-lineStart)
return nil
}
pos++
obj := new(dataObject)
obj.tag = tag
obj.property = []DataNode{}
for pos < size {
var node DataNode
skipSpaces(true)
if data[pos] == '}' {
pos++
skipSpaces(false)
return obj
}
if node = parseNode(); node == nil {
return nil
}
obj.property = append(obj.property, node)
if data[pos] == '}' {
pos++
skipSpaces(true)
return obj
} else if data[pos] != ',' && data[pos] != '\n' {
ErrorLogF(`Expected '}', '\n' or ',' (line: %d, position: %d)`, line, pos-lineStart)
return nil
}
if data[pos] != '\n' {
pos++
}
skipSpaces(true)
for data[pos] == ',' {
pos++
skipSpaces(true)
}
}
ErrorLog("Unexpected end of text")
return nil
}
parseArray = func() []DataValue {
pos++
skipSpaces(true)
array := []DataValue{}
for pos < size {
var tag string
var ok bool
skipSpaces(true)
for data[pos] == ',' && pos < size {
pos++
skipSpaces(true)
}
if pos >= size {
break
}
if data[pos] == ']' {
pos++
skipSpaces(true)
return array
}
if tag, ok = parseTag(); !ok {
return nil
}
if data[pos] == '{' {
obj := parseObject(tag)
if obj == nil {
return nil
}
array = append(array, obj)
} else {
val := new(dataStringValue)
val.value = tag
array = append(array, val)
}
switch data[pos] {
case ']', ',', '\n':
default:
ErrorLogF("Expected ']' or ',' (line: %d, position: %d)", line, pos-lineStart)
return nil
}
/*
if data[pos] == ']' {
pos++
skipSpaces()
return array, nil
} else if data[pos] != ',' {
return nil, fmt.Errorf("Expected ']' or ',' (line: %d, position: %d)", line, pos-lineStart)
}
pos++
skipSpaces()
*/
}
ErrorLog("Unexpected end of text")
return nil
}
if tag, ok := parseTag(); ok {
return parseObject(tag)
}
return nil
} }

View File

@ -6,10 +6,6 @@ import (
func TestParseDataText(t *testing.T) { func TestParseDataText(t *testing.T) {
SetErrorLog(func(text string) {
t.Error(text)
})
text := `obj1 { text := `obj1 {
key1 = val1, key1 = val1,
key2=obj2{ key2=obj2{
@ -27,8 +23,10 @@ func TestParseDataText(t *testing.T) {
key3 = "\n \t \\ \r \" ' \X4F\x4e \U01Ea",` + key3 = "\n \t \\ \r \" ' \X4F\x4e \U01Ea",` +
"key4=`" + `\n \t \\ \r \" ' \x8F \UF80a` + "`\r}" "key4=`" + `\n \t \\ \r \" ' \x8F \UF80a` + "`\r}"
obj := ParseDataText(text) obj, err := ParseDataText(text)
if obj != nil { if err != nil {
t.Error(err)
} else {
if obj.Tag() != "obj1" { if obj.Tag() != "obj1" {
t.Error(`obj.Tag() != "obj1"`) t.Error(`obj.Tag() != "obj1"`)
} }
@ -173,9 +171,6 @@ func TestParseDataText(t *testing.T) {
} }
} }
SetErrorLog(func(text string) {
})
failText := []string{ failText := []string{
" ", " ",
"obj[]", "obj[]",
@ -204,7 +199,7 @@ func TestParseDataText(t *testing.T) {
} }
for _, txt := range failText { for _, txt := range failText {
if obj := ParseDataText(txt); obj != nil { if _, err := ParseDataText(txt); err == nil {
t.Errorf("result ParseDataText(\"%s\") must be fail", txt) t.Errorf("result ParseDataText(\"%s\") must be fail", txt)
} }
} }

View File

@ -941,6 +941,25 @@ func NewPopup(view View, param Params) Popup {
return popup return popup
} }
/*
func CreatePopupFromObject(session Session, object DataObject, binding any) Popup {
node := object.RemovePropertyByTag(string(Content))
if node == nil {
ErrorLog(`"content" property not found`)
return nil
}
switch node.Type() {
case ObjectNode:
case TextNode:
default:
ErrorLog(`Unsupported data of "content" property`)
return nil
}
}
*/
// ShowPopup creates a new Popup and shows it // ShowPopup creates a new Popup and shows it
func ShowPopup(view View, param Params) Popup { func ShowPopup(view View, param Params) Popup {
popup := NewPopup(view, param) popup := NewPopup(view, param)

View File

@ -266,7 +266,8 @@ func (session *sessionData) setBridge(events chan DataObject, bridge bridge) {
func (session *sessionData) close() { func (session *sessionData) close() {
if session.events != nil { if session.events != nil {
session.events <- ParseDataText(`session-close{session="` + strconv.Itoa(session.sessionID) + `"}`) obj, _ := ParseDataText(`session-close{session="` + strconv.Itoa(session.sessionID) + `"}`)
session.events <- obj
} }
} }

View File

@ -50,8 +50,9 @@ func (resources *resourceManager) scanStringsDir(path string) {
} }
func loadStringResources(text string) { func loadStringResources(text string) {
data := ParseDataText(text) data, err := ParseDataText(text)
if data == nil { if err != nil {
ErrorLog(err.Error())
return return
} }

View File

@ -686,8 +686,11 @@ func (theme *theme) addText(themeText string) bool {
theme.init() theme.init()
} }
data := ParseDataText(themeText) data, err := ParseDataText(themeText)
if data == nil || !data.IsObject() || data.Tag() != "theme" { if err != nil {
ErrorLog(err.Error())
return false
} else if !data.IsObject() || data.Tag() != "theme" {
return false return false
} }

View File

@ -398,7 +398,10 @@ func valueToTransformProperty(value any) TransformProperty {
} }
case string: case string:
if obj := ParseDataText(value); obj != nil { obj, err := ParseDataText(value)
if err != nil {
ErrorLog(err.Error())
} else {
return parseObject(obj) return parseObject(obj)
} }
} }

View File

@ -117,14 +117,17 @@ func CreateViewFromObject(session Session, object DataObject, binding any) View
// //
// If the function fails, it returns nil and an error message is written to the log. // If the function fails, it returns nil and an error message is written to the log.
func CreateViewFromText(session Session, text string, binding ...any) View { func CreateViewFromText(session Session, text string, binding ...any) View {
if data := ParseDataText(text); data != nil { data, err := ParseDataText(text)
var b any = nil if err != nil {
if len(binding) > 0 { ErrorLog(err.Error())
b = binding[0] return nil
}
return CreateViewFromObject(session, data, b)
} }
return nil
var b any = nil
if len(binding) > 0 {
b = binding[0]
}
return CreateViewFromObject(session, data, b)
} }
// CreateViewFromResources create new View and initialize it by the content of // CreateViewFromResources create new View and initialize it by the content of
@ -153,14 +156,20 @@ func CreateViewFromResources(session Session, name string, binding ...any) View
case viewDir: case viewDir:
if data, err := fs.ReadFile(dir + "/" + name); err == nil { if data, err := fs.ReadFile(dir + "/" + name); err == nil {
if data := ParseDataText(string(data)); data != nil { data, err := ParseDataText(string(data))
if err != nil {
ErrorLog(err.Error())
} else {
return CreateViewFromObject(session, data, b) return CreateViewFromObject(session, data, b)
} }
} }
default: default:
if data, err := fs.ReadFile(dir + "/" + viewDir + "/" + name); err == nil { if data, err := fs.ReadFile(dir + "/" + viewDir + "/" + name); err == nil {
if data := ParseDataText(string(data)); data != nil { data, err := ParseDataText(string(data))
if err != nil {
ErrorLog(err.Error())
} else {
return CreateViewFromObject(session, data, b) return CreateViewFromObject(session, data, b)
} }
} }
@ -170,7 +179,10 @@ func CreateViewFromResources(session Session, name string, binding ...any) View
if resources.path != "" { if resources.path != "" {
if data, err := os.ReadFile(resources.path + viewDir + "/" + name); err == nil { if data, err := os.ReadFile(resources.path + viewDir + "/" + name); err == nil {
if data := ParseDataText(string(data)); data != nil { data, err := ParseDataText(string(data))
if err != nil {
ErrorLog(err.Error())
} else {
return CreateViewFromObject(session, data, b) return CreateViewFromObject(session, data, b)
} }
} }

View File

@ -173,11 +173,9 @@ func (container *viewsContainerData) htmlSubviews(self View, buffer *strings.Bui
} }
func viewFromTextValue(text string, session Session) View { func viewFromTextValue(text string, session Session) View {
if strings.Contains(text, "{") && strings.Contains(text, "}") { if data, err := ParseDataText(text); err == nil {
if data := ParseDataText(text); data != nil { if view := CreateViewFromObject(session, data, nil); view != nil {
if view := CreateViewFromObject(session, data, nil); view != nil { return view
return view
}
} }
} }
return NewTextView(session, Params{Text: text}) return NewTextView(session, Params{Text: text})