Repository: goplus/gop
Pull request #2231 (2025-03-31 04:04:31)

tpl/matcher: QSTRING, RAWSTRING; rm CSTRING, PYSTRING


Patch:
diff --git a/doc/spec/mini/mini.gop b/doc/spec/mini/mini.gop
index a3825c0e..3dd6833e 100644
--- a/doc/spec/mini/mini.gop
+++ b/doc/spec/mini/mini.gop
@@ -169,9 +169,9 @@ PrimaryExpr = Operand *(
 	CallOrConversion | SelectorOrTypeAssertion | IndexOrSlice | ErrWrap)
 
 Operand =
-	INT ?UNIT | FLOAT ?UNIT | STRING | CHAR | CSTRING | PYSTRING | RAT | IMAG |
+	INT ?UNIT | FLOAT ?UNIT | STRING | CHAR | RAT | IMAG |
 	"(" Expression ")" | LiteralValue | CompositeLit | FunctionLit | Env |
-	DomainTextLit | NamedCompositeLit | IDENT
+	"c" ++ QSTRING | "py" ++ QSTRING | DomainTextLit | NamedCompositeLit | IDENT
 
 Env = "$" ("{" IDENT "}" | IDENT)
 
diff --git a/tpl/cl/compile.go b/tpl/cl/compile.go
index 0543b66c..88162925 100644
--- a/tpl/cl/compile.go
+++ b/tpl/cl/compile.go
@@ -160,18 +160,16 @@ func LogConflict(pos token.Position, firsts [][]any, i, at int) {
 
 var (
 	idents = map[string]token.Token{
-		"EOF":      token.EOF,
-		"COMMENT":  token.COMMENT,
-		"IDENT":    token.IDENT,
-		"INT":      token.INT,
-		"FLOAT":    token.FLOAT,
-		"IMAG":     token.IMAG,
-		"CHAR":     token.CHAR,
-		"STRING":   token.STRING,
-		"CSTRING":  token.CSTRING,
-		"PYSTRING": token.PYSTRING,
-		"RAT":      token.RAT,
-		"UNIT":     token.UNIT,
+		"EOF":     token.EOF,
+		"COMMENT": token.COMMENT,
+		"IDENT":   token.IDENT,
+		"INT":     token.INT,
+		"FLOAT":   token.FLOAT,
+		"IMAG":    token.IMAG,
+		"CHAR":    token.CHAR,
+		"STRING":  token.STRING,
+		"RAT":     token.RAT,
+		"UNIT":    token.UNIT,
 	}
 )
 
@@ -181,13 +179,19 @@ func compileExpr(expr ast.Expr, ctx *context) (matcher.Matcher, bool) {
 		name := expr.Name
 		if v, ok := ctx.rules[name]; ok {
 			return v, true
-		}
-		if tok, ok := idents[name]; ok {
+		} else if tok, ok := idents[name]; ok {
 			return matcher.Token(tok), true
-		} else if name == "RAWSTRING" {
-			return matcher.RawString(), true
 		}
-		ctx.addErrorf(expr.Pos(), "`%s` is undefined", name)
+		var quoteCh byte
+		switch name {
+		case "RAWSTRING":
+			quoteCh = '`'
+		case "QSTRING":
+			quoteCh = '"'
+		default:
+			ctx.addErrorf(expr.Pos(), "`%s` is undefined", name)
+		}
+		return matcher.String(quoteCh), true
 	case *ast.BasicLit:
 		lit := expr.Value
 		switch expr.Kind {
diff --git a/tpl/matcher/match.go b/tpl/matcher/match.go
index c744d978..c2e04328 100644
--- a/tpl/matcher/match.go
+++ b/tpl/matcher/match.go
@@ -226,30 +226,37 @@ func True() Matcher {
 
 // -----------------------------------------------------------------------------
 
-type gRawString struct{}
+type gString byte
 
-func (p gRawString) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {
+func (p gString) Match(src []*types.Token, ctx *Context) (n int, result any, err error) {
 	if len(src) == 0 {
-		return 0, nil, ctx.NewErrorf(ctx.FileEnd, "expect `RAWSTRING`, but got EOF")
+		return 0, nil, ctx.NewErrorf(ctx.FileEnd, "expect `%s`, but got EOF", stringType(p))
 	}
 	t := src[0]
-	if t.Tok != token.STRING || t.Lit[0] != '`' {
-		return 0, nil, ctx.NewErrorf(t.Pos, "expect `RAWSTRING`, but got `%v`", t)
+	if t.Tok != token.STRING || t.Lit[0] != byte(p) {
+		return 0, nil, ctx.NewErrorf(t.Pos, "expect `%s`, but got `%v`", stringType(p), t)
 	}
 	return 1, t, nil
 }
 
-func (p gRawString) First(in []any) (first []any, mayEmpty bool) {
+func (p gString) First(in []any) (first []any, mayEmpty bool) {
 	return append(in, token.STRING), false
 }
 
-func (p gRawString) IsList() bool {
+func (p gString) IsList() bool {
 	return false
 }
 
-// RawString returns a matcher that matches a raw string literal.
-func RawString() Matcher {
-	return gRawString{}
+// String returns a matcher that matches a string literal.
+func String(quoteCh byte) Matcher {
+	return gString(quoteCh)
+}
+
+func stringType(quoteCh gString) string {
+	if quoteCh == '"' {
+		return "QSTRING"
+	}
+	return "RAWSTRING"
 }
 
 // -----------------------------------------------------------------------------
diff --git a/tpl/parser/parser.go b/tpl/parser/parser.go
index 113fdf59..f99eaf00 100644
--- a/tpl/parser/parser.go
+++ b/tpl/parser/parser.go
@@ -223,7 +223,7 @@ func (p *parser) lambdaExpr() (start, end token.Pos, ok bool) {
 	}
 }
 
-// parseExpr parses an expression: +term % '|'
+// parseExpr: termList % '|'
 func (p *parser) parseExpr() ast.Expr {
 	termList := p.parseTermList()
 	for p.tok != token.OR {
@@ -240,7 +240,7 @@ func (p *parser) parseExpr() ast.Expr {
 	return &ast.Choice{Options: options}
 }
 
-// parseTermList parses a list of terms: +term
+// parseTermList: +term
 func (p *parser) parseTermList() ast.Expr {
 	terms := make([]ast.Expr, 0, 1)
 	for {
@@ -261,7 +261,7 @@ func (p *parser) parseTermList() ast.Expr {
 	}
 }
 
-// parseTerm parses a term: term2 % '%'
+// parseTerm: term2 % '%'
 func (p *parser) parseTerm() (ast.Expr, bool) {
 	x, ok := p.parseTerm2()
 	if !ok {
@@ -286,7 +286,7 @@ func (p *parser) parseTerm() (ast.Expr, bool) {
 	return x, true
 }
 
-// parseTerm2 parses a term2: factor % "++"
+// parseTerm2: factor % "++"
 func (p *parser) parseTerm2() (ast.Expr, bool) {
 	x, ok := p.parseFactor()
 	if !ok {
diff --git a/tpl/scanner/scanner.go b/tpl/scanner/scanner.go
index 2c9d1a11..1d2a714e 100644
--- a/tpl/scanner/scanner.go
+++ b/tpl/scanner/scanner.go
@@ -649,23 +649,11 @@ scanAgain:
 	switch ch := s.ch; {
 	case isLetter(ch):
 		t.Lit = s.scanIdentifier()
-		if (t.Lit == "c" || t.Lit == "C") && s.ch == '"' { // c"..."
-			s.next()
-			insertSemi = true
-			t.Tok = token.CSTRING
-			t.Lit = s.scanString()
-		} else if t.Lit == "py" && s.ch == '"' { // py"..."
-			s.next()
-			insertSemi = true
-			t.Tok = token.PYSTRING
-			t.Lit = s.scanString()
-		} else {
-			insertSemi = true
-			t.Tok = token.IDENT
-		}
-	case '0' <= ch && ch <= '9':
+		t.Tok = token.IDENT
 		insertSemi = true
+	case '0' <= ch && ch <= '9':
 		t.Tok, t.Lit = s.scanNumber(false)
+		insertSemi = true
 	default:
 		s.next() // always make progress
 		switch ch {
diff --git a/tpl/token/token.go b/tpl/token/token.go
index 18dfdadf..a3101773 100644
--- a/tpl/token/token.go
+++ b/tpl/token/token.go
@@ -55,16 +55,14 @@ const (
 	literal_beg
 	// Identifiers and basic type literals
 	// (these tokens stand for classes of literals)
-	IDENT    // main
-	INT      // 12345
-	FLOAT    // 123.45
-	IMAG     // 123.45i
-	CHAR     // 'a'
-	STRING   // "abc"
-	CSTRING  // c"Hello"
-	PYSTRING // py"Hello"
-	RAT      // 3r, 3.4r
-	UNIT     // 1m, 2.3s, 3ms, 4us, 5ns, 6.5m, 7h, 8d, 9w, 10y
+	IDENT  // main
+	INT    // 12345
+	FLOAT  // 123.45
+	IMAG   // 123.45i
+	CHAR   // 'a'
+	STRING // "abc"
+	RAT    // 3r, 3.4r
+	UNIT   // 1m, 2.3s, 3ms, 4us, 5ns, 6.5m, 7h, 8d, 9w, 10y
 	literal_end
 
 	ADD = '+'
@@ -148,16 +146,14 @@ var tokens = [...]string{
 	EOF:     "EOF",
 	COMMENT: "COMMENT",
 
-	IDENT:    "IDENT",
-	INT:      "INT",
-	FLOAT:    "FLOAT",
-	IMAG:     "IMAG",
-	CHAR:     "CHAR",
-	STRING:   "STRING",
-	CSTRING:  "CSTRING",
-	PYSTRING: "PYSTRING",
-	RAT:      "RAT",
-	UNIT:     "UNIT",
+	IDENT:  "IDENT",
+	INT:    "INT",
+	FLOAT:  "FLOAT",
+	IMAG:   "IMAG",
+	CHAR:   "CHAR",
+	STRING: "STRING",
+	RAT:    "RAT",
+	UNIT:   "UNIT",
 
 	ADD: "+",
 	SUB: "-",


Test patch:
diff --git a/tpl/scanner/_testdata/cstr/tpl.expect b/tpl/scanner/_testdata/cstr/tpl.expect
index 16ea4142..aee852ee 100644
--- a/tpl/scanner/_testdata/cstr/tpl.expect
+++ b/tpl/scanner/_testdata/cstr/tpl.expect
@@ -1,12 +1,14 @@
 1 IDENT c
 2 . 
 3 IDENT printf
-10 CSTRING "Hello"
+10 IDENT c
+11 STRING "Hello"
 18 ; 
 
 19 IDENT std
 22 . 
 23 IDENT print
-29 PYSTRING "Hello"
+29 IDENT py
+31 STRING "Hello"
 38 ;