From a3335110dc4f1283cf1680e273cc9775b7e8c8ac Mon Sep 17 00:00:00 2001 From: _ov4 Date: Tue, 20 Oct 2026 11:42:45 +0000 Subject: feat(lexer): add C_STRING literal suffix Co-Authored-By: Claude Opus 5.5 --- include/cps/AST.h | 4 +++- include/cps/Lexer.h | 1 + lib/CodeGen/ValueTypeGen.cc | 3 ++- lib/Lexer/Lexer.cc | 4 ++++ lib/Parser/ParserControlFlow.cc | 2 +- lib/Parser/ParserExpression.cc | 4 ++-- lib/Parser/ParserType.cc | 2 +- tests/i/c-string-literals/case.json | 1 + tests/i/c-string-literals/expected.txt | 4 ++++ tests/i/c-string-literals/notes.md | 5 +++++ tests/i/c-string-literals/program.cps | 13 +++++++++++++ 11 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 tests/i/c-string-literals/case.json create mode 100644 tests/i/c-string-literals/expected.txt create mode 100644 tests/i/c-string-literals/notes.md create mode 100644 tests/i/c-string-literals/program.cps diff --git a/include/cps/AST.h b/include/cps/AST.h index 2b78aaa..bb6d090 100644 --- a/include/cps/AST.h +++ b/include/cps/AST.h @@ -51,9 +51,11 @@ public: class StringExprAST : public ExprAST { std::string Val; + bool CString; public: - StringExprAST(const std::string &Val) : Val(Val) {} + StringExprAST(const std::string &Val, bool CString = false) : Val(Val), CString(CString) {} const std::string &getVal() const { return Val; } + bool isCString() const { return CString; } }; class VariableExprAST : public ExprAST { diff --git a/include/cps/Lexer.h b/include/cps/Lexer.h index 588a5fa..b514904 100644 --- a/include/cps/Lexer.h +++ b/include/cps/Lexer.h @@ -49,6 +49,7 @@ enum Token { tok_string_kw = -30, tok_string_literal = -36, + tok_c_string_literal = -37, tok_char_kw = -38, tok_char_literal = -39, diff --git a/lib/CodeGen/ValueTypeGen.cc b/lib/CodeGen/ValueTypeGen.cc index e7a3f77..f2dafb1 100644 --- a/lib/CodeGen/ValueTypeGen.cc +++ b/lib/CodeGen/ValueTypeGen.cc @@ -15,7 +15,8 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) { if (dynamic_cast(Expr)) return resolveType("REAL"); if (dynamic_cast(Expr)) return resolveType("BOOLEAN"); if (dynamic_cast(Expr)) return resolveType("CHAR"); - if (dynamic_cast(Expr)) return resolveType("STRING"); + if (auto *String = dynamic_cast(Expr)) + return resolveType(String->isCString() ? "C_STRING" : "STRING"); if (auto *Cast = dynamic_cast(Expr)) return resolveType(Cast->getTypeName()); diff --git a/lib/Lexer/Lexer.cc b/lib/Lexer/Lexer.cc index 577a741..6d0246a 100644 --- a/lib/Lexer/Lexer.cc +++ b/lib/Lexer/Lexer.cc @@ -137,6 +137,10 @@ int Lexer::readToken() { } CharVal = StringVal.size() == 1 ? StringVal[0] : '?'; } + if (!IsChar && LastChar == 'C') { + LastChar = Input.get(); + return tok_c_string_literal; + } return IsChar ? tok_char_literal : tok_string_literal; } diff --git a/lib/Parser/ParserControlFlow.cc b/lib/Parser/ParserControlFlow.cc index 10dd512..8ca024d 100644 --- a/lib/Parser/ParserControlFlow.cc +++ b/lib/Parser/ParserControlFlow.cc @@ -157,7 +157,7 @@ bool Parser::isCaseLabel() { case '(': case '[': ++Depth; break; case ')': case ']': if (--Depth < 0) return false; break; case tok_identifier: case tok_number_int: case tok_number_real: - case tok_string_literal: case tok_char_literal: case tok_true: case tok_false: + case tok_string_literal: case tok_c_string_literal: case tok_char_literal: case tok_true: case tok_false: case tok_integer_kw: case tok_real_kw: case tok_boolean_kw: case tok_string_kw: case tok_char_kw: case tok_c_int: case tok_c_char: case tok_c_string: case tok_c_double: diff --git a/lib/Parser/ParserExpression.cc b/lib/Parser/ParserExpression.cc index 7558c86..be22b4e 100644 --- a/lib/Parser/ParserExpression.cc +++ b/lib/Parser/ParserExpression.cc @@ -216,8 +216,8 @@ std::unique_ptr Parser::ParsePrimaryImpl() { case tok_identifier: case tok_operator: return ParseIdentifierExpr(); case tok_number_int: return ParseNumberExpr(); case tok_number_real: return ParseNumberExpr(); - case tok_string_literal: { - auto Res = std::make_unique(Lex.StringVal); + case tok_string_literal: case tok_c_string_literal: { + auto Res = std::make_unique(Lex.StringVal, CurTok == tok_c_string_literal); getNextToken(); return Res; } diff --git a/lib/Parser/ParserType.cc b/lib/Parser/ParserType.cc index d281293..e3adc5b 100644 --- a/lib/Parser/ParserType.cc +++ b/lib/Parser/ParserType.cc @@ -346,7 +346,7 @@ std::unique_ptr Parser::ParseConstant() { if (Negative) getNextToken(); bool Numeric = CurTok == tok_number_int || CurTok == tok_number_real; bool Literal = Numeric || CurTok == tok_true || CurTok == tok_false || - CurTok == tok_string_literal || CurTok == tok_char_literal; + CurTok == tok_string_literal || CurTok == tok_c_string_literal || CurTok == tok_char_literal; if (!Literal || (Negative && !Numeric)) { cps::sourceDiagnostic(Lex.getLine(), "error: CONSTANT initializer must be a literal at line %d\n", Line); return nullptr; diff --git a/tests/i/c-string-literals/case.json b/tests/i/c-string-literals/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/c-string-literals/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/c-string-literals/expected.txt b/tests/i/c-string-literals/expected.txt new file mode 100644 index 0000000..2255027 --- /dev/null +++ b/tests/i/c-string-literals/expected.txt @@ -0,0 +1,4 @@ +quote:" slash:\ +AB +0 +1:2 diff --git a/tests/i/c-string-literals/notes.md b/tests/i/c-string-literals/notes.md new file mode 100644 index 0000000..a469ce1 --- /dev/null +++ b/tests/i/c-string-literals/notes.md @@ -0,0 +1,5 @@ +# C string literals + +Human review: pending. Expectations follow the literal bytes and declared overloads. + +The adjacent uppercase C suffix produces C_STRING. Escapes use the ordinary lexer rules. Explicit STRING casts preserve the text; an empty C string has length zero after conversion. Overload selection distinguishes STRING from C_STRING. diff --git a/tests/i/c-string-literals/program.cps b/tests/i/c-string-literals/program.cps new file mode 100644 index 0000000..ac2f7b0 --- /dev/null +++ b/tests/i/c-string-literals/program.cps @@ -0,0 +1,13 @@ +DECLARE Text : C_STRING +Text <- "quote:\" slash:\\"C +OUTPUT (STRING)Text +OUTPUT (STRING)"\101\x42"C +CONSTANT Empty = ""C +OUTPUT LENGTH((STRING)Empty) +OVERLOAD FUNCTION Kind(Text : STRING) RETURNS INTEGER + RETURN 1 +ENDFUNCTION +OVERLOAD FUNCTION Kind(Text : C_STRING) RETURNS INTEGER + RETURN 2 +ENDFUNCTION +OUTPUT Kind("text"), ":", Kind("text"C) -- cgit v1.3.1-4-g156e