| // lex.h -- Go frontend lexer. -*- C++ -*- |
| |
| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| #ifndef GO_LEX_H |
| #define GO_LEX_H |
| |
| #include <mpfr.h> |
| |
| #include "operator.h" |
| #include "go-linemap.h" |
| |
| struct Unicode_range; |
| |
| // The keywords. These must be in sorted order, other than |
| // KEYWORD_INVALID. They must match the Keywords::mapping_ array in |
| // lex.cc. |
| |
| enum Keyword |
| { |
| KEYWORD_INVALID, // Not a keyword. |
| KEYWORD_ASM, |
| KEYWORD_BREAK, |
| KEYWORD_CASE, |
| KEYWORD_CHAN, |
| KEYWORD_CONST, |
| KEYWORD_CONTINUE, |
| KEYWORD_DEFAULT, |
| KEYWORD_DEFER, |
| KEYWORD_ELSE, |
| KEYWORD_FALLTHROUGH, |
| KEYWORD_FOR, |
| KEYWORD_FUNC, |
| KEYWORD_GO, |
| KEYWORD_GOTO, |
| KEYWORD_IF, |
| KEYWORD_IMPORT, |
| KEYWORD_INTERFACE, |
| KEYWORD_MAP, |
| KEYWORD_PACKAGE, |
| KEYWORD_RANGE, |
| KEYWORD_RETURN, |
| KEYWORD_SELECT, |
| KEYWORD_STRUCT, |
| KEYWORD_SWITCH, |
| KEYWORD_TYPE, |
| KEYWORD_VAR |
| }; |
| |
| // Pragmas built from magic comments and recorded for functions. |
| // These are used as bits in a bitmask. |
| // The set of values is intended to be the same as the gc compiler. |
| |
| enum GoPragma |
| { |
| GOPRAGMA_NOINTERFACE = 1 << 0, // Method not in type descriptor. |
| GOPRAGMA_NOESCAPE = 1 << 1, // Args do not escape. |
| GOPRAGMA_NORACE = 1 << 2, // No race detector. |
| GOPRAGMA_NOSPLIT = 1 << 3, // Do not split stack. |
| GOPRAGMA_NOINLINE = 1 << 4, // Do not inline. |
| GOPRAGMA_SYSTEMSTACK = 1 << 5, // Must run on system stack. |
| GOPRAGMA_NOWRITEBARRIER = 1 << 6, // No write barriers. |
| GOPRAGMA_NOWRITEBARRIERREC = 1 << 7, // No write barriers here or callees. |
| GOPRAGMA_YESWRITEBARRIERREC = 1 << 8, // Stops nowritebarrierrec. |
| GOPRAGMA_MARK = 1 << 9, // Marker for nowritebarrierrec. |
| GOPRAGMA_CGOUNSAFEARGS = 1 << 10, // Pointer to arg is pointer to all. |
| GOPRAGMA_UINTPTRESCAPES = 1 << 11, // uintptr(p) escapes. |
| GOPRAGMA_NOTINHEAP = 1 << 12 // type is not in heap. |
| }; |
| |
| // A token returned from the lexer. |
| |
| class Token |
| { |
| public: |
| // Token classification. |
| enum Classification |
| { |
| // Token is invalid. |
| TOKEN_INVALID, |
| // Token indicates end of input. |
| TOKEN_EOF, |
| // Token is a keyword. |
| TOKEN_KEYWORD, |
| // Token is an identifier. |
| TOKEN_IDENTIFIER, |
| // Token is a string of characters. |
| TOKEN_STRING, |
| // Token is an operator. |
| TOKEN_OPERATOR, |
| // Token is a character constant. |
| TOKEN_CHARACTER, |
| // Token is an integer. |
| TOKEN_INTEGER, |
| // Token is a floating point number. |
| TOKEN_FLOAT, |
| // Token is an imaginary number. |
| TOKEN_IMAGINARY |
| }; |
| |
| ~Token(); |
| Token(const Token&); |
| Token& operator=(const Token&); |
| |
| // Get token classification. |
| Classification |
| classification() const |
| { return this->classification_; } |
| |
| // Make a token for an invalid value. |
| static Token |
| make_invalid_token(Location location) |
| { return Token(TOKEN_INVALID, location); } |
| |
| // Make a token representing end of file. |
| static Token |
| make_eof_token(Location location) |
| { return Token(TOKEN_EOF, location); } |
| |
| // Make a keyword token. |
| static Token |
| make_keyword_token(Keyword keyword, Location location) |
| { |
| Token tok(TOKEN_KEYWORD, location); |
| tok.u_.keyword = keyword; |
| return tok; |
| } |
| |
| // Make an identifier token. |
| static Token |
| make_identifier_token(const std::string& value, bool is_exported, |
| Location location) |
| { |
| Token tok(TOKEN_IDENTIFIER, location); |
| tok.u_.identifier_value.name = new std::string(value); |
| tok.u_.identifier_value.is_exported = is_exported; |
| return tok; |
| } |
| |
| // Make a quoted string token. |
| static Token |
| make_string_token(const std::string& value, Location location) |
| { |
| Token tok(TOKEN_STRING, location); |
| tok.u_.string_value = new std::string(value); |
| return tok; |
| } |
| |
| // Make an operator token. |
| static Token |
| make_operator_token(Operator op, Location location) |
| { |
| Token tok(TOKEN_OPERATOR, location); |
| tok.u_.op = op; |
| return tok; |
| } |
| |
| // Make a character constant token. |
| static Token |
| make_character_token(mpz_t val, Location location) |
| { |
| Token tok(TOKEN_CHARACTER, location); |
| mpz_init(tok.u_.integer_value); |
| mpz_swap(tok.u_.integer_value, val); |
| return tok; |
| } |
| |
| // Make an integer token. |
| static Token |
| make_integer_token(mpz_t val, Location location) |
| { |
| Token tok(TOKEN_INTEGER, location); |
| mpz_init(tok.u_.integer_value); |
| mpz_swap(tok.u_.integer_value, val); |
| return tok; |
| } |
| |
| // Make a float token. |
| static Token |
| make_float_token(mpfr_t val, Location location) |
| { |
| Token tok(TOKEN_FLOAT, location); |
| mpfr_init(tok.u_.float_value); |
| mpfr_swap(tok.u_.float_value, val); |
| return tok; |
| } |
| |
| // Make a token for an imaginary number. |
| static Token |
| make_imaginary_token(mpfr_t val, Location location) |
| { |
| Token tok(TOKEN_IMAGINARY, location); |
| mpfr_init(tok.u_.float_value); |
| mpfr_swap(tok.u_.float_value, val); |
| return tok; |
| } |
| |
| // Get the location of the token. |
| Location |
| location() const |
| { return this->location_; } |
| |
| // Return whether this is an invalid token. |
| bool |
| is_invalid() const |
| { return this->classification_ == TOKEN_INVALID; } |
| |
| // Return whether this is the EOF token. |
| bool |
| is_eof() const |
| { return this->classification_ == TOKEN_EOF; } |
| |
| // Return the keyword value for a keyword token. |
| Keyword |
| keyword() const |
| { |
| go_assert(this->classification_ == TOKEN_KEYWORD); |
| return this->u_.keyword; |
| } |
| |
| // Return whether this is an identifier. |
| bool |
| is_identifier() const |
| { return this->classification_ == TOKEN_IDENTIFIER; } |
| |
| // Return the identifier. |
| const std::string& |
| identifier() const |
| { |
| go_assert(this->classification_ == TOKEN_IDENTIFIER); |
| return *this->u_.identifier_value.name; |
| } |
| |
| // Return whether the identifier is exported. |
| bool |
| is_identifier_exported() const |
| { |
| go_assert(this->classification_ == TOKEN_IDENTIFIER); |
| return this->u_.identifier_value.is_exported; |
| } |
| |
| // Return whether this is a string. |
| bool |
| is_string() const |
| { |
| return this->classification_ == TOKEN_STRING; |
| } |
| |
| // Return the value of a string. The returned value is a string of |
| // UTF-8 characters. |
| std::string |
| string_value() const |
| { |
| go_assert(this->classification_ == TOKEN_STRING); |
| return *this->u_.string_value; |
| } |
| |
| // Return the value of a character constant. |
| const mpz_t* |
| character_value() const |
| { |
| go_assert(this->classification_ == TOKEN_CHARACTER); |
| return &this->u_.integer_value; |
| } |
| |
| // Return the value of an integer. |
| const mpz_t* |
| integer_value() const |
| { |
| go_assert(this->classification_ == TOKEN_INTEGER); |
| return &this->u_.integer_value; |
| } |
| |
| // Return the value of a float. |
| const mpfr_t* |
| float_value() const |
| { |
| go_assert(this->classification_ == TOKEN_FLOAT); |
| return &this->u_.float_value; |
| } |
| |
| // Return the value of an imaginary number. |
| const mpfr_t* |
| imaginary_value() const |
| { |
| go_assert(this->classification_ == TOKEN_IMAGINARY); |
| return &this->u_.float_value; |
| } |
| |
| // Return the operator value for an operator token. |
| Operator |
| op() const |
| { |
| go_assert(this->classification_ == TOKEN_OPERATOR); |
| return this->u_.op; |
| } |
| |
| // Return whether this token is KEYWORD. |
| bool |
| is_keyword(Keyword keyword) const |
| { |
| return (this->classification_ == TOKEN_KEYWORD |
| && this->u_.keyword == keyword); |
| } |
| |
| // Return whether this token is OP. |
| bool |
| is_op(Operator op) const |
| { return this->classification_ == TOKEN_OPERATOR && this->u_.op == op; } |
| |
| // Print the token for debugging. |
| void |
| print(FILE*) const; |
| |
| private: |
| // Private constructor used by make_..._token functions above. |
| Token(Classification, Location); |
| |
| // Clear the token. |
| void |
| clear(); |
| |
| // The token classification. |
| Classification classification_; |
| union |
| { |
| // The keyword value for TOKEN_KEYWORD. |
| Keyword keyword; |
| // The token value for TOKEN_IDENTIFIER. |
| struct |
| { |
| // The name of the identifier. This has been mangled to only |
| // include ASCII characters. |
| std::string* name; |
| // Whether this name should be exported. This is true if the |
| // first letter in the name is upper case. |
| bool is_exported; |
| } identifier_value; |
| // The string value for TOKEN_STRING. |
| std::string* string_value; |
| // The token value for TOKEN_CHARACTER or TOKEN_INTEGER. |
| mpz_t integer_value; |
| // The token value for TOKEN_FLOAT or TOKEN_IMAGINARY. |
| mpfr_t float_value; |
| // The token value for TOKEN_OPERATOR or the keyword value |
| Operator op; |
| } u_; |
| // The source location. |
| Location location_; |
| }; |
| |
| // The lexer itself. |
| |
| class Lex |
| { |
| public: |
| Lex(const char* input_file_name, FILE* input_file, Linemap *linemap); |
| |
| ~Lex(); |
| |
| // Return the next token. |
| Token |
| next_token(); |
| |
| // Return the contents of any current //extern comment. |
| const std::string& |
| extern_name() const |
| { return this->extern_; } |
| |
| // Return the current set of pragmas, and clear them. |
| unsigned int |
| get_and_clear_pragmas() |
| { |
| unsigned int ret = this->pragmas_; |
| this->pragmas_ = 0; |
| return ret; |
| } |
| |
| struct Linkname |
| { |
| std::string ext_name; // External name; empty to just export. |
| bool is_exported; // Whether the internal name is exported. |
| Location loc; // Location of go:linkname directive. |
| |
| Linkname() |
| : ext_name(), is_exported(false), loc() |
| { } |
| |
| Linkname(const std::string& ext_name_a, bool is_exported_a, Location loc_a) |
| : ext_name(ext_name_a), is_exported(is_exported_a), loc(loc_a) |
| { } |
| }; |
| |
| typedef std::map<std::string, Linkname> Linknames; |
| |
| // Return the linknames seen so far, or NULL if none, and clear the |
| // set. These are from go:linkname compiler directives. |
| Linknames* |
| get_and_clear_linknames() |
| { |
| Linknames* ret = this->linknames_; |
| this->linknames_ = NULL; |
| return ret; |
| } |
| |
| // Return whether there are any current go:embed patterns. |
| bool |
| has_embeds() const |
| { return !this->embeds_.empty(); } |
| |
| // If there are any go:embed patterns seen so far, store them in |
| // *EMBEDS and clear the saved set. *EMBEDS must be an empty |
| // vector. |
| void |
| get_and_clear_embeds(std::vector<std::string>* embeds) |
| { |
| go_assert(embeds->empty()); |
| std::swap(*embeds, this->embeds_); |
| } |
| |
| // Return whether the identifier NAME should be exported. NAME is a |
| // mangled name which includes only ASCII characters. |
| static bool |
| is_exported_mangled_name(const std::string& name); |
| |
| // Return whether the identifier NAME should be exported. NAME is |
| // an unmangled utf-8 string and may contain non-ASCII characters. |
| static bool |
| is_exported_name(const std::string& name); |
| |
| // Return whether the identifier NAME is invalid. When we see an |
| // invalid character we still build an identifier, but we use a |
| // magic string to indicate that the identifier is invalid. We then |
| // use this to avoid knockon errors. |
| static bool |
| is_invalid_identifier(const std::string& name); |
| |
| // A helper function. Append V to STR. IS_CHARACTER is true if V |
| // is a Unicode character which should be converted into UTF-8, |
| // false if it is a byte value to be appended directly. The |
| // location is used to warn about an out of range character. |
| static void |
| append_char(unsigned int v, bool is_charater, std::string* str, |
| Location); |
| |
| // A helper function. Fetch a UTF-8 character from STR and store it |
| // in *VALUE. Return the number of bytes read from STR. Return 0 |
| // if STR does not point to a valid UTF-8 character. |
| static int |
| fetch_char(const char* str, unsigned int *value); |
| |
| // Return whether C is a Unicode or "C" locale space character. |
| static bool |
| is_unicode_space(unsigned int c); |
| |
| // Convert the specified hex char into an unsigned integer value. |
| static unsigned |
| hex_val(char c); |
| |
| private: |
| ssize_t |
| get_line(); |
| |
| bool |
| require_line(); |
| |
| // The current location. |
| Location |
| location() const; |
| |
| // A position CHARS column positions before the current location. |
| Location |
| earlier_location(int chars) const; |
| |
| static bool |
| is_hex_digit(char); |
| |
| static bool |
| is_base_digit(int base, char); |
| |
| static unsigned char |
| octal_value(char c) |
| { return c - '0'; } |
| |
| Token |
| make_invalid_token() |
| { return Token::make_invalid_token(this->location()); } |
| |
| Token |
| make_eof_token() |
| { return Token::make_eof_token(this->location()); } |
| |
| Token |
| make_operator(Operator op, int chars) |
| { return Token::make_operator_token(op, this->earlier_location(chars)); } |
| |
| Token |
| gather_identifier(); |
| |
| static bool |
| could_be_exponent(int base, const char*, const char*); |
| |
| Token |
| gather_number(); |
| |
| void |
| skip_exponent(); |
| |
| Token |
| gather_character(); |
| |
| Token |
| gather_string(); |
| |
| Token |
| gather_raw_string(); |
| |
| const char* |
| advance_one_utf8_char(const char*, unsigned int*, bool*); |
| |
| const char* |
| advance_one_char(const char*, bool, unsigned int*, bool*); |
| |
| static bool |
| is_unicode_digit(unsigned int c); |
| |
| static bool |
| is_unicode_letter(unsigned int c); |
| |
| static bool |
| is_unicode_uppercase(unsigned int c); |
| |
| static bool |
| is_in_unicode_range(unsigned int C, const Unicode_range* ranges, |
| size_t range_size); |
| |
| Operator |
| three_character_operator(char, char, char); |
| |
| Operator |
| two_character_operator(char, char); |
| |
| Operator |
| one_character_operator(char); |
| |
| bool |
| skip_c_comment(bool* found_newline); |
| |
| void |
| skip_cpp_comment(); |
| |
| void |
| gather_embed(const char*, const char*); |
| |
| // The input file name. |
| const char* input_file_name_; |
| // The input file. |
| FILE* input_file_; |
| // The object used to keep track of file names and line numbers. |
| Linemap* linemap_; |
| // The line buffer. This holds the current line. |
| char* linebuf_; |
| // The size of the line buffer. |
| size_t linebufsize_; |
| // The nmber of characters in the current line. |
| size_t linesize_; |
| // The current offset in linebuf_. |
| size_t lineoff_; |
| // The current line number. |
| size_t lineno_; |
| // Whether to add a semicolon if we see a newline now. |
| bool add_semi_at_eol_; |
| // Pragmas for the next function, from magic comments. |
| unsigned int pragmas_; |
| // The external name to use for a function declaration, from a magic |
| // //extern comment. |
| std::string extern_; |
| // The list of //go:linkname comments, if any. |
| Linknames* linknames_; |
| // The list of //go:embed patterns, if any. |
| std::vector<std::string> embeds_; |
| }; |
| |
| #endif // !defined(GO_LEX_H) |