mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 07:54:41 +01:00
Add string parsing and a print builtin (#721)
It was in my mind to add String in order to support libraries in `package`. `print` is added in order to have a String go to stdout. I've tried to do `print` in a way that won't be too hard to add other printable types, but it's probably also somewhat optional here -- that is, if desired, I could remove it. But it was a lot easier to doublecheck `\n` behavior with it, and I suspect it'll be helpful in other tests if it supports more value types. On the side, this also fixes dereferencing in Pattern/Expression Print() calls, which I was noticing printing pointers instead of values. This may be another argument for moving away from passing pointers, since this seems to be a difficult-to-catch error. Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
committed by
GitHub
co-authored by
Geoff Romer
parent
ecb5a611e5
commit
250ce4ab00
@@ -67,7 +67,7 @@ static void PrintFields(llvm::raw_ostream& out,
|
||||
const std::vector<FieldInitializer>& fields) {
|
||||
llvm::ListSeparator sep;
|
||||
for (const auto& field : fields) {
|
||||
out << sep << field.name << " = " << field.expression;
|
||||
out << sep << field.name << " = " << *field.expression;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,14 @@ void Expression::Print(llvm::raw_ostream& out) const {
|
||||
case Expression::Kind::IntTypeLiteral:
|
||||
out << "i32";
|
||||
break;
|
||||
case Expression::Kind::StringLiteral:
|
||||
out << "\"";
|
||||
out.write_escaped(cast<StringLiteral>(*this).Val());
|
||||
out << "\"";
|
||||
break;
|
||||
case Expression::Kind::StringTypeLiteral:
|
||||
out << "String";
|
||||
break;
|
||||
case Expression::Kind::TypeTypeLiteral:
|
||||
out << "Type";
|
||||
break;
|
||||
@@ -140,6 +148,13 @@ void Expression::Print(llvm::raw_ostream& out) const {
|
||||
out << "fn " << *fn.Parameter() << " -> " << *fn.ReturnType();
|
||||
break;
|
||||
}
|
||||
case Expression::Kind::IntrinsicExpression:
|
||||
out << "intrinsic_expression(";
|
||||
switch (cast<IntrinsicExpression>(*this).Intrinsic()) {
|
||||
case IntrinsicExpression::IntrinsicKind::Print:
|
||||
out << "print";
|
||||
}
|
||||
out << ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,12 @@ class Expression {
|
||||
ContinuationTypeLiteral, // The type of a continuation value.
|
||||
IntLiteral,
|
||||
PrimitiveOperatorExpression,
|
||||
StringLiteral,
|
||||
StringTypeLiteral,
|
||||
TupleLiteral,
|
||||
TypeTypeLiteral,
|
||||
IdentifierExpression,
|
||||
IntrinsicExpression,
|
||||
};
|
||||
|
||||
// Returns the enumerator corresponding to the most-derived type of this
|
||||
@@ -178,6 +181,31 @@ class BoolLiteral : public Expression {
|
||||
bool val;
|
||||
};
|
||||
|
||||
class StringLiteral : public Expression {
|
||||
public:
|
||||
explicit StringLiteral(int line_num, std::string val)
|
||||
: Expression(Kind::StringLiteral, line_num), val(std::move(val)) {}
|
||||
|
||||
static auto classof(const Expression* exp) -> bool {
|
||||
return exp->Tag() == Kind::StringLiteral;
|
||||
}
|
||||
|
||||
auto Val() const -> const std::string& { return val; }
|
||||
|
||||
private:
|
||||
std::string val;
|
||||
};
|
||||
|
||||
class StringTypeLiteral : public Expression {
|
||||
public:
|
||||
explicit StringTypeLiteral(int line_num)
|
||||
: Expression(Kind::StringTypeLiteral, line_num) {}
|
||||
|
||||
static auto classof(const Expression* exp) -> bool {
|
||||
return exp->Tag() == Kind::StringTypeLiteral;
|
||||
}
|
||||
};
|
||||
|
||||
class TupleLiteral : public Expression {
|
||||
public:
|
||||
explicit TupleLiteral(int line_num) : TupleLiteral(line_num, {}) {}
|
||||
@@ -301,6 +329,25 @@ class TypeTypeLiteral : public Expression {
|
||||
}
|
||||
};
|
||||
|
||||
class IntrinsicExpression : public Expression {
|
||||
public:
|
||||
enum class IntrinsicKind {
|
||||
Print,
|
||||
};
|
||||
|
||||
explicit IntrinsicExpression(IntrinsicKind intrinsic)
|
||||
: Expression(Kind::IntrinsicExpression, -1), intrinsic(intrinsic) {}
|
||||
|
||||
static auto classof(const Expression* exp) -> bool {
|
||||
return exp->Tag() == Kind::IntrinsicExpression;
|
||||
}
|
||||
|
||||
auto Intrinsic() const -> IntrinsicKind { return intrinsic; }
|
||||
|
||||
private:
|
||||
IntrinsicKind intrinsic;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // EXECUTABLE_SEMANTICS_AST_EXPRESSION_H_
|
||||
|
||||
@@ -37,19 +37,19 @@ void Pattern::Print(llvm::raw_ostream& out) const {
|
||||
out << "(";
|
||||
llvm::ListSeparator sep;
|
||||
for (const TuplePattern::Field& field : tuple.Fields()) {
|
||||
out << sep << field.name << " = " << field.pattern;
|
||||
out << sep << field.name << " = " << *field.pattern;
|
||||
}
|
||||
out << ")";
|
||||
break;
|
||||
}
|
||||
case Kind::AlternativePattern: {
|
||||
const auto& alternative = cast<AlternativePattern>(*this);
|
||||
out << alternative.ChoiceType() << "." << alternative.AlternativeName()
|
||||
<< alternative.Arguments();
|
||||
out << *alternative.ChoiceType() << "." << alternative.AlternativeName()
|
||||
<< *alternative.Arguments();
|
||||
break;
|
||||
}
|
||||
case Kind::ExpressionPattern:
|
||||
out << cast<ExpressionPattern>(*this).Expression();
|
||||
out << *cast<ExpressionPattern>(*this).Expression();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,15 @@ auto CurrentEnv(State* state) -> Env {
|
||||
return frame->scopes.Top()->values;
|
||||
}
|
||||
|
||||
// Returns the given name from the environment, printing an error if not found.
|
||||
static auto GetFromEnv(int line_num, const std::string& name) -> Address {
|
||||
std::optional<Address> pointer = CurrentEnv(state).Get(name);
|
||||
if (!pointer) {
|
||||
FATAL_RUNTIME_ERROR(line_num) << "could not find `" << name << "`";
|
||||
}
|
||||
return *pointer;
|
||||
}
|
||||
|
||||
void PrintState(llvm::raw_ostream& out) {
|
||||
out << "{\nstack: ";
|
||||
PrintStack(state->stack, out);
|
||||
@@ -411,14 +420,9 @@ void StepLvalue() {
|
||||
case Expression::Kind::IdentifierExpression: {
|
||||
// { {x :: C, E, F} :: S, H}
|
||||
// -> { {E(x) :: C, E, F} :: S, H}
|
||||
std::optional<Address> pointer =
|
||||
CurrentEnv(state).Get(cast<IdentifierExpression>(*exp).Name());
|
||||
if (!pointer) {
|
||||
FATAL_RUNTIME_ERROR(exp->LineNumber())
|
||||
<< "could not find `" << cast<IdentifierExpression>(*exp).Name()
|
||||
<< "`";
|
||||
}
|
||||
const Value* v = global_arena->New<PointerValue>(*pointer);
|
||||
Address pointer = GetFromEnv(exp->LineNumber(),
|
||||
cast<IdentifierExpression>(*exp).Name());
|
||||
const Value* v = global_arena->New<PointerValue>(pointer);
|
||||
frame->todo.Pop();
|
||||
frame->todo.Push(global_arena->New<ValAction>(v));
|
||||
break;
|
||||
@@ -496,10 +500,12 @@ void StepLvalue() {
|
||||
case Expression::Kind::BoolTypeLiteral:
|
||||
case Expression::Kind::TypeTypeLiteral:
|
||||
case Expression::Kind::FunctionTypeLiteral:
|
||||
case Expression::Kind::ContinuationTypeLiteral: {
|
||||
case Expression::Kind::ContinuationTypeLiteral:
|
||||
case Expression::Kind::StringLiteral:
|
||||
case Expression::Kind::StringTypeLiteral:
|
||||
case Expression::Kind::IntrinsicExpression:
|
||||
FATAL_RUNTIME_ERROR_NO_LINE()
|
||||
<< "Can't treat expression as lvalue: " << *exp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,12 +603,8 @@ void StepExp() {
|
||||
CHECK(act->Pos() == 0);
|
||||
const auto& ident = cast<IdentifierExpression>(*exp);
|
||||
// { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H}
|
||||
std::optional<Address> pointer = CurrentEnv(state).Get(ident.Name());
|
||||
if (!pointer) {
|
||||
FATAL_RUNTIME_ERROR(exp->LineNumber())
|
||||
<< "could not find `" << ident.Name() << "`";
|
||||
}
|
||||
const Value* pointee = state->heap.Read(*pointer, exp->LineNumber());
|
||||
Address pointer = GetFromEnv(exp->LineNumber(), ident.Name());
|
||||
const Value* pointee = state->heap.Read(pointer, exp->LineNumber());
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(global_arena->New<ValAction>(pointee));
|
||||
break;
|
||||
@@ -660,6 +662,22 @@ void StepExp() {
|
||||
FATAL() << "in handle_value with Call pos " << act->Pos();
|
||||
}
|
||||
break;
|
||||
case Expression::Kind::IntrinsicExpression:
|
||||
CHECK(act->Pos() == 0);
|
||||
// { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H}
|
||||
frame->todo.Pop(1);
|
||||
switch (cast<IntrinsicExpression>(*exp).Intrinsic()) {
|
||||
case IntrinsicExpression::IntrinsicKind::Print:
|
||||
Address pointer = GetFromEnv(exp->LineNumber(), "format_str");
|
||||
const Value* pointee = state->heap.Read(pointer, exp->LineNumber());
|
||||
CHECK(pointee->Tag() == Value::Kind::StringValue);
|
||||
// TODO: This could eventually use something like llvm::formatv.
|
||||
llvm::outs() << cast<StringValue>(*pointee).Val();
|
||||
frame->todo.Push(global_arena->New<ValAction>(&TupleValue::Empty()));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Expression::Kind::IntTypeLiteral: {
|
||||
CHECK(act->Pos() == 0);
|
||||
const Value* v = global_arena->New<IntType>();
|
||||
@@ -710,6 +728,20 @@ void StepExp() {
|
||||
frame->todo.Push(global_arena->New<ValAction>(v));
|
||||
break;
|
||||
}
|
||||
case Expression::Kind::StringLiteral:
|
||||
CHECK(act->Pos() == 0);
|
||||
// { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H}
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(global_arena->New<ValAction>(
|
||||
global_arena->New<StringValue>(cast<StringLiteral>(*exp).Val())));
|
||||
break;
|
||||
case Expression::Kind::StringTypeLiteral: {
|
||||
CHECK(act->Pos() == 0);
|
||||
const Value* v = global_arena->New<StringType>();
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(global_arena->New<ValAction>(v));
|
||||
break;
|
||||
}
|
||||
} // switch (exp->Tag)
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,19 @@ static auto ReifyType(const Value* t, int line_num) -> const Expression* {
|
||||
case Value::Kind::VariableType:
|
||||
return global_arena->New<IdentifierExpression>(
|
||||
0, cast<VariableType>(*t).Name());
|
||||
default:
|
||||
case Value::Kind::StringType:
|
||||
return global_arena->New<StringTypeLiteral>(0);
|
||||
case Value::Kind::AlternativeConstructorValue:
|
||||
case Value::Kind::AlternativeValue:
|
||||
case Value::Kind::AutoType:
|
||||
case Value::Kind::BindingPlaceholderValue:
|
||||
case Value::Kind::BoolValue:
|
||||
case Value::Kind::ContinuationValue:
|
||||
case Value::Kind::FunctionValue:
|
||||
case Value::Kind::IntValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::StringValue:
|
||||
case Value::Kind::StructValue:
|
||||
FATAL() << "expected a type, not " << *t;
|
||||
}
|
||||
}
|
||||
@@ -158,10 +170,10 @@ static auto ArgumentDeduction(int line_num, TypeEnv deduced, const Value* param,
|
||||
case Value::Kind::ChoiceType:
|
||||
case Value::Kind::IntType:
|
||||
case Value::Kind::BoolType:
|
||||
case Value::Kind::TypeType: {
|
||||
case Value::Kind::TypeType:
|
||||
case Value::Kind::StringType:
|
||||
ExpectType(line_num, "argument deduction", param, arg);
|
||||
return deduced;
|
||||
}
|
||||
// The rest of these cases should never happen.
|
||||
case Value::Kind::IntValue:
|
||||
case Value::Kind::BoolValue:
|
||||
@@ -172,6 +184,7 @@ static auto ArgumentDeduction(int line_num, TypeEnv deduced, const Value* param,
|
||||
case Value::Kind::BindingPlaceholderValue:
|
||||
case Value::Kind::AlternativeConstructorValue:
|
||||
case Value::Kind::ContinuationValue:
|
||||
case Value::Kind::StringValue:
|
||||
FATAL() << "In ArgumentDeduction: expected type, not value " << *param;
|
||||
}
|
||||
}
|
||||
@@ -213,6 +226,7 @@ static auto Substitute(TypeEnv dict, const Value* type) -> const Value* {
|
||||
case Value::Kind::StructType:
|
||||
case Value::Kind::ChoiceType:
|
||||
case Value::Kind::ContinuationType:
|
||||
case Value::Kind::StringType:
|
||||
return type;
|
||||
// The rest of these cases should never happen.
|
||||
case Value::Kind::IntValue:
|
||||
@@ -224,6 +238,7 @@ static auto Substitute(TypeEnv dict, const Value* type) -> const Value* {
|
||||
case Value::Kind::BindingPlaceholderValue:
|
||||
case Value::Kind::AlternativeConstructorValue:
|
||||
case Value::Kind::ContinuationValue:
|
||||
case Value::Kind::StringValue:
|
||||
FATAL() << "In Substitute: expected type, not value " << *type;
|
||||
}
|
||||
}
|
||||
@@ -475,12 +490,17 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values)
|
||||
/*is_omitted_return_type=*/false);
|
||||
return TCExpression(new_e, global_arena->New<TypeType>(), types);
|
||||
}
|
||||
case Expression::Kind::StringLiteral:
|
||||
return TCExpression(e, global_arena->New<StringType>(), types);
|
||||
case Expression::Kind::IntrinsicExpression:
|
||||
switch (cast<IntrinsicExpression>(*e).Intrinsic()) {
|
||||
case IntrinsicExpression::IntrinsicKind::Print:
|
||||
return TCExpression(e, &TupleValue::Empty(), types);
|
||||
}
|
||||
case Expression::Kind::IntTypeLiteral:
|
||||
return TCExpression(e, global_arena->New<TypeType>(), types);
|
||||
case Expression::Kind::BoolTypeLiteral:
|
||||
return TCExpression(e, global_arena->New<TypeType>(), types);
|
||||
case Expression::Kind::StringTypeLiteral:
|
||||
case Expression::Kind::TypeTypeLiteral:
|
||||
return TCExpression(e, global_arena->New<TypeType>(), types);
|
||||
case Expression::Kind::ContinuationTypeLiteral:
|
||||
return TCExpression(e, global_arena->New<TypeType>(), types);
|
||||
}
|
||||
|
||||
@@ -236,6 +236,14 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
// TODO: Find a way to print useful information about the continuation
|
||||
// without creating a dependency cycle.
|
||||
break;
|
||||
case Value::Kind::StringType:
|
||||
out << "String";
|
||||
break;
|
||||
case Value::Kind::StringValue:
|
||||
out << "\"";
|
||||
out.write_escaped(cast<StringValue>(*this).Val());
|
||||
out << "\"";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,6 +301,10 @@ auto CopyVal(const Value* val, int line_num) -> const Value* {
|
||||
return global_arena->New<AutoType>();
|
||||
case Value::Kind::ContinuationType:
|
||||
return global_arena->New<ContinuationType>();
|
||||
case Value::Kind::StringType:
|
||||
return global_arena->New<StringType>();
|
||||
case Value::Kind::StringValue:
|
||||
return global_arena->New<StringValue>(cast<StringValue>(*val).Val());
|
||||
case Value::Kind::VariableType:
|
||||
case Value::Kind::StructType:
|
||||
case Value::Kind::ChoiceType:
|
||||
@@ -339,6 +351,7 @@ auto TypeEqual(const Value* t1, const Value* t2) -> bool {
|
||||
case Value::Kind::BoolType:
|
||||
case Value::Kind::ContinuationType:
|
||||
case Value::Kind::TypeType:
|
||||
case Value::Kind::StringType:
|
||||
return true;
|
||||
case Value::Kind::VariableType:
|
||||
return cast<VariableType>(*t1).Name() == cast<VariableType>(*t2).Name();
|
||||
@@ -390,6 +403,8 @@ auto ValueEqual(const Value* v1, const Value* v2, int line_num) -> bool {
|
||||
case Value::Kind::TupleValue:
|
||||
return FieldsValueEqual(cast<TupleValue>(*v1).Elements(),
|
||||
cast<TupleValue>(*v2).Elements(), line_num);
|
||||
case Value::Kind::StringValue:
|
||||
return cast<StringValue>(*v1).Val() == cast<StringValue>(*v2).Val();
|
||||
case Value::Kind::IntType:
|
||||
case Value::Kind::BoolType:
|
||||
case Value::Kind::TypeType:
|
||||
@@ -400,6 +415,7 @@ auto ValueEqual(const Value* v1, const Value* v2, int line_num) -> bool {
|
||||
case Value::Kind::ChoiceType:
|
||||
case Value::Kind::ContinuationType:
|
||||
case Value::Kind::VariableType:
|
||||
case Value::Kind::StringType:
|
||||
return TypeEqual(v1, v2);
|
||||
case Value::Kind::StructValue:
|
||||
case Value::Kind::AlternativeValue:
|
||||
|
||||
@@ -48,10 +48,12 @@ class Value {
|
||||
StructType,
|
||||
ChoiceType,
|
||||
ContinuationType, // The type of a continuation.
|
||||
VariableType, // e.g. generic type parameters
|
||||
VariableType, // e.g., generic type parameters.
|
||||
BindingPlaceholderValue,
|
||||
AlternativeConstructorValue,
|
||||
ContinuationValue // A first-class continuation value.
|
||||
ContinuationValue, // A first-class continuation value.
|
||||
StringType,
|
||||
StringValue,
|
||||
};
|
||||
|
||||
Value(const Value&) = delete;
|
||||
@@ -442,6 +444,32 @@ class ContinuationValue : public Value {
|
||||
std::vector<Frame*> stack;
|
||||
};
|
||||
|
||||
// The String type.
|
||||
class StringType : public Value {
|
||||
public:
|
||||
StringType() : Value(Kind::StringType) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->Tag() == Kind::StringType;
|
||||
}
|
||||
};
|
||||
|
||||
// A string value.
|
||||
class StringValue : public Value {
|
||||
public:
|
||||
explicit StringValue(std::string val)
|
||||
: Value(Kind::StringValue), val(std::move(val)) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->Tag() == Kind::StringValue;
|
||||
}
|
||||
|
||||
auto Val() const -> const std::string& { return val; }
|
||||
|
||||
private:
|
||||
std::string val;
|
||||
};
|
||||
|
||||
auto CopyVal(const Value* val, int line_num) -> const Value*;
|
||||
|
||||
auto TypeEqual(const Value* t1, const Value* t2) -> bool;
|
||||
|
||||
@@ -31,6 +31,7 @@ cc_library(
|
||||
":paren_contents",
|
||||
"//common:check",
|
||||
"//common:ostream",
|
||||
"//common:string_helpers",
|
||||
"//executable_semantics/ast:declaration",
|
||||
"//executable_semantics/ast:expression",
|
||||
"//executable_semantics/common:arena",
|
||||
|
||||
@@ -8,6 +8,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#include <cstdlib>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "common/string_helpers.h"
|
||||
#include "executable_semantics/common/tracing_flag.h"
|
||||
#include "executable_semantics/syntax/parse_and_lex_context.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
@@ -49,6 +50,7 @@ MATCH "match"
|
||||
NOT "not"
|
||||
OR "or"
|
||||
RETURN "return"
|
||||
STRING "String"
|
||||
STRUCT "struct"
|
||||
TRUE "true"
|
||||
TYPE "Type"
|
||||
@@ -63,6 +65,7 @@ UNDERSCORE "_"
|
||||
identifier [A-Za-z_][A-Za-z0-9_]*
|
||||
sized_type_literal [iuf][1-9][0-9]*
|
||||
integer_literal [0-9]+
|
||||
string_literal \"([^\\\"\n]|\\.)*\"
|
||||
horizontal_whitespace [ \t\r]
|
||||
whitespace [ \t\r\n]
|
||||
operand_start [(A-Za-z0-9_"]
|
||||
@@ -120,6 +123,7 @@ operand_start [(A-Za-z0-9_"]
|
||||
{RUN} { return Carbon::Parser::make_RUN(context.current_token_position); }
|
||||
{AWAIT} { return Carbon::Parser::make_AWAIT(context.current_token_position); }
|
||||
{UNDERSCORE} { return Carbon::Parser::make_UNDERSCORE(context.current_token_position); }
|
||||
{STRING} { return Carbon::Parser::make_STRING(context.current_token_position); }
|
||||
|
||||
{sized_type_literal} { return Carbon::Parser::make_sized_type_literal(yytext, context.current_token_position); }
|
||||
|
||||
@@ -189,6 +193,23 @@ operator and its operand, leading to three more cases:
|
||||
return Carbon::Parser::make_integer_literal(val, context.current_token_position);
|
||||
}
|
||||
|
||||
{string_literal} {
|
||||
llvm::StringRef str(yytext);
|
||||
CHECK(str.consume_front("\"") && str.consume_back("\""));
|
||||
std::optional<std::string> unescaped = Carbon::UnescapeStringLiteral(str);
|
||||
if (unescaped == std::nullopt) {
|
||||
if (Carbon::tracing_output) {
|
||||
// Print a newline because tracing prints an incomplete line
|
||||
// "Reading a token: ".
|
||||
llvm::errs() << "\n";
|
||||
}
|
||||
FATAL_COMPILATION_ERROR(yylineno)
|
||||
<< "Invalid escaping in string: " << yytext;
|
||||
}
|
||||
return Carbon::Parser::make_string_literal(
|
||||
*unescaped, context.current_token_position);
|
||||
}
|
||||
|
||||
{ONE_LINE_COMMENT} {
|
||||
// Advance end by 1 line, resetting the column to zero.
|
||||
context.current_token_position.lines(1);
|
||||
|
||||
@@ -4,12 +4,16 @@
|
||||
|
||||
#include "executable_semantics/syntax/parse_and_lex_context.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
// Writes a syntax error diagnostic, containing message, for the input file at
|
||||
// the given line, to standard error.
|
||||
auto Carbon::ParseAndLexContext::PrintDiagnostic(const std::string& message,
|
||||
int line_num) -> void {
|
||||
auto ParseAndLexContext::PrintDiagnostic(const std::string& message,
|
||||
int line_num) -> void {
|
||||
// TODO: Do we really want this to be fatal? It makes the comment and the
|
||||
// name a lie, and renders some of the other yyparse() result propagation code
|
||||
// moot.
|
||||
FATAL_COMPILATION_ERROR(line_num) << message;
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -90,6 +90,7 @@ void Carbon::Parser::error(const location_type&, const std::string& message) {
|
||||
%token <int> integer_literal
|
||||
%token <std::string> identifier
|
||||
%token <std::string> sized_type_literal
|
||||
%token <std::string> string_literal
|
||||
%type <std::string> designator
|
||||
%type <const Declaration*> declaration
|
||||
%type <FunctionDefinition> function_declaration
|
||||
@@ -131,6 +132,7 @@ void Carbon::Parser::error(const location_type&, const std::string& message) {
|
||||
%token AND
|
||||
%token OR
|
||||
%token NOT
|
||||
%token STRING
|
||||
%token BOOL
|
||||
%token TYPE
|
||||
%token FN
|
||||
@@ -218,6 +220,8 @@ expression:
|
||||
{ $$ = global_arena->New<IndexExpression>(yylineno, $1, $3); }
|
||||
| integer_literal
|
||||
{ $$ = global_arena->New<IntLiteral>(yylineno, $1); }
|
||||
| string_literal
|
||||
{ $$ = global_arena->New<StringLiteral>(yylineno, $1); }
|
||||
| TRUE
|
||||
{ $$ = global_arena->New<BoolLiteral>(yylineno, true); }
|
||||
| FALSE
|
||||
@@ -229,6 +233,8 @@ expression:
|
||||
CHECK($1[0] == 'i' && val == 32) << "Only i32 is supported for now: " << $1;
|
||||
$$ = global_arena->New<IntTypeLiteral>(yylineno);
|
||||
}
|
||||
| STRING
|
||||
{ $$ = global_arena->New<StringTypeLiteral>(yylineno); }
|
||||
| BOOL
|
||||
{ $$ = global_arena->New<BoolTypeLiteral>(yylineno); }
|
||||
| TYPE
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "executable_semantics/syntax/syntax_helpers.h"
|
||||
|
||||
#include "common/check.h"
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/common/arena.h"
|
||||
#include "executable_semantics/common/tracing_flag.h"
|
||||
@@ -12,7 +13,29 @@
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
void ExecProgram(const std::list<const Declaration*>& fs) {
|
||||
// Adds builtins, currently only Print(). Note Print() is experimental, not
|
||||
// standardized, but is made available for printing state in tests.
|
||||
static void AddIntrinsics(std::list<const Declaration*>* fs) {
|
||||
std::vector<TuplePattern::Field> print_fields = {TuplePattern::Field(
|
||||
"0", global_arena->New<BindingPattern>(
|
||||
-1, "format_str",
|
||||
global_arena->New<ExpressionPattern>(
|
||||
global_arena->New<StringTypeLiteral>(-1))))};
|
||||
auto* print_return =
|
||||
global_arena->New<Return>(-1,
|
||||
global_arena->New<IntrinsicExpression>(
|
||||
IntrinsicExpression::IntrinsicKind::Print),
|
||||
false);
|
||||
auto* print = global_arena->New<FunctionDeclaration>(FunctionDefinition(
|
||||
-1, "Print", std::vector<GenericBinding>(),
|
||||
global_arena->New<TuplePattern>(-1, print_fields),
|
||||
global_arena->New<ExpressionPattern>(global_arena->New<TupleLiteral>(-1)),
|
||||
/*is_omitted_return_type=*/false, print_return));
|
||||
fs->insert(fs->begin(), print);
|
||||
}
|
||||
|
||||
void ExecProgram(std::list<const Declaration*> fs) {
|
||||
AddIntrinsics(&fs);
|
||||
if (tracing_output) {
|
||||
llvm::outs() << "********** source program **********\n";
|
||||
for (const auto* decl : fs) {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
namespace Carbon {
|
||||
|
||||
// Runs the top-level declaration list.
|
||||
void ExecProgram(const std::list<const Declaration*>& fs);
|
||||
void ExecProgram(std::list<const Declaration*> fs);
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ TEST_LIST = [
|
||||
"pattern_init",
|
||||
"pattern_variable_fail",
|
||||
"placeholder_variable",
|
||||
"print1",
|
||||
"record1",
|
||||
"return_auto",
|
||||
"return_empty_explicit",
|
||||
@@ -74,6 +75,14 @@ TEST_LIST = [
|
||||
"return_empty_implicit_fail1",
|
||||
"return_empty_implicit_fail2",
|
||||
"star",
|
||||
"string1",
|
||||
"string2",
|
||||
"string3",
|
||||
"string_fail1",
|
||||
"string_fail2",
|
||||
"string_fail3",
|
||||
"string_fail4",
|
||||
"string_fail5",
|
||||
"struct1",
|
||||
"struct2",
|
||||
"struct3",
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
fn main() -> i32 {
|
||||
var s: auto = "Hello world!\n";
|
||||
Print(s);
|
||||
return 0;
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Hello world!
|
||||
result: 0
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
fn CompareStr(s: String) -> i32 {
|
||||
if (s == "str") {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
return CompareStr("str");
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
result: 0
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
fn CompareStr(s: String) -> i32 {
|
||||
if (s == "str\n") {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
return CompareStr("str\n");
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
result: 0
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
fn CompareStr(s: String) -> i32 {
|
||||
if (s == "str;") {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
return CompareStr("\x73\x74\x72\x3B");
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
result: 0
|
||||
@@ -0,0 +1,8 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
fn main() -> i32 {
|
||||
Print("str\e");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
COMPILATION ERROR: 6: Invalid escaping in string: "str\e"
|
||||
EXIT CODE: 255
|
||||
@@ -0,0 +1,8 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
fn main() -> i32 {
|
||||
Print("str\x");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
COMPILATION ERROR: 6: Invalid escaping in string: "str\x"
|
||||
EXIT CODE: 255
|
||||
@@ -0,0 +1,8 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
fn main() -> i32 {
|
||||
Print("str\xaa");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
COMPILATION ERROR: 6: Invalid escaping in string: "str\xaa"
|
||||
EXIT CODE: 255
|
||||
@@ -0,0 +1,8 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
fn main() -> i32 {
|
||||
Print("str\01");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
COMPILATION ERROR: 6: Invalid escaping in string: "str\01"
|
||||
EXIT CODE: 255
|
||||
@@ -0,0 +1,9 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
fn main() -> i32 {
|
||||
Print("new
|
||||
line");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
COMPILATION ERROR: 6: invalid character '\x22' in source file.
|
||||
EXIT CODE: 255
|
||||
Reference in New Issue
Block a user