X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=parser.c;h=9a094bdb950afebbf40dfa61b7dae5199ad67965;hb=cdf58c2b9caf708944a94cbe3060bcc120fdf287;hp=f823725a18af60a974a07f48923a36cf5c2f6294;hpb=b4e35bf2387e9b22ae6a97bad28daa8a438483ed;p=cparser diff --git a/parser.c b/parser.c index f823725..9a094bd 100644 --- a/parser.c +++ b/parser.c @@ -3,16 +3,40 @@ #include #include +#include "parser.h" #include "lexer_t.h" #include "token_t.h" #include "type_t.h" +#include "type_hash.h" #include "ast_t.h" #include "adt/bitfiddle.h" #include "adt/error.h" +#include "adt/array.h" #define PRINT_TOKENS -static token_t token; +struct environment_entry_t { + symbol_t *symbol; + environment_entry_t *old_entry; + declaration_t *declaration; + unsigned short old_symbol_ID; +}; + +static token_t token; +static struct obstack environment_obstack; +static environment_entry_t **environment_stack = NULL; +static translation_unit_t *translation_unit = NULL; +static block_statement_t *context = NULL; + +static +statement_t *parse_compound_statement(void); +static +statement_t *parse_statement(void); + +static +expression_t *parse_sub_expression(unsigned precedence); +static +expression_t *parse_expression(void); static inline void *allocate_ast_zero(size_t size) @@ -22,6 +46,92 @@ void *allocate_ast_zero(size_t size) return res; } +static inline +void *allocate_type_zero(size_t size) +{ + void *res = obstack_alloc(type_obst, size); + memset(res, 0, size); + return res; +} + +/** + * pushs an environment_entry on the environment stack and links the + * corresponding symbol to the new entry + */ +static inline +environment_entry_t *environment_push(symbol_t *symbol) +{ + environment_entry_t *entry + = obstack_alloc(&environment_obstack, sizeof(entry[0])); + memset(entry, 0, sizeof(entry[0])); + + int top = ARR_LEN(environment_stack); + ARR_RESIZE(environment_stack, top + 1); + environment_stack[top] = entry; + + entry->old_entry = symbol->thing; + entry->symbol = symbol; + symbol->thing = entry; + + return entry; +} + +/** + * pops symbols from the environment stack until @p new_top is the top element + */ +static inline +void environment_pop_to(size_t new_top) +{ + environment_entry_t *entry = NULL; + size_t top = ARR_LEN(environment_stack); + size_t i; + + if(new_top == top) + return; + + assert(new_top < top); + i = top; + do { + entry = environment_stack[i - 1]; + + symbol_t *symbol = entry->symbol; + +#if 0 + if(entry->type == ENTRY_LOCAL_VARIABLE + && entry->e.variable->refs == 0) { + variable_declaration_statement_t *variable = entry->e.variable; + print_warning_prefix(env, variable->statement.source_position); + fprintf(stderr, "variable '%s' was declared but never read\n", + symbol->string); + } +#endif + + if(entry->declaration->storage_class == STORAGE_CLASS_TYPEDEF) { + fprintf(stderr, "pop typename '%s'\n", entry->symbol->string); + symbol->ID = entry->old_symbol_ID; + } + + assert(symbol->thing == entry); + symbol->thing = entry->old_entry; + + --i; + } while(i != new_top); + obstack_free(&environment_obstack, entry); + + ARR_SHRINKLEN(environment_stack, (int) new_top); +} + +/** + * returns the top element of the environment stack + */ +static inline +size_t environment_top() +{ + return ARR_LEN(environment_stack); +} + + + static inline void next_token(void) { @@ -104,6 +214,77 @@ void eat_until_semi(void) } \ next_token(); +#define expect_void(expected) \ + if(UNLIKELY(token.type != (expected))) { \ + parse_error_expected(NULL, (expected), 0); \ + eat_until_semi(); \ + return; \ + } \ + next_token(); + +static expression_t *parse_constant_expression(void) +{ + /* TODO: not correct yet */ + return parse_expression(); +} + +static compound_entry_t *parse_compound_type_entries(void); + +typedef struct declaration_specifiers_t declaration_specifiers_t; +struct declaration_specifiers_t { + storage_class_t storage_class; + type_t *type; +}; + +static type_t *parse_struct_specifier(void) +{ + eat(T_struct); + + compound_type_t *struct_type = allocate_type_zero(sizeof(struct_type[0])); + struct_type->type.type = TYPE_COMPOUND_STRUCT; + struct_type->source_position = source_position; + + if(token.type == T_IDENTIFIER) { + /* TODO */ + next_token(); + if(token.type == '{') { + parse_compound_type_entries(); + } + } else if(token.type == '{') { + parse_compound_type_entries(); + } else { + parse_error_expected("problem while parsing struct type specifiers", + T_IDENTIFIER, '{'); + return NULL; + } + + return (type_t*) struct_type; +} + +static type_t *parse_union_specifier(void) +{ + eat(T_union); + + compound_type_t *union_type = allocate_type_zero(sizeof(union_type[0])); + union_type->type.type = TYPE_COMPOUND_UNION; + union_type->source_position = source_position; + + if(token.type == T_IDENTIFIER) { + /* TODO */ + next_token(); + if(token.type == '{') { + parse_compound_type_entries(); + } + } else if(token.type == '{') { + parse_compound_type_entries(); + } else { + parse_error_expected("problem while parsing union type specifiers", + T_IDENTIFIER, '{'); + } + + return (type_t*) union_type; +} + typedef enum { SPECIFIER_SIGNED = 1 << 0, SPECIFIER_UNSIGNED = 1 << 1, @@ -124,34 +305,61 @@ typedef enum { #endif } specifiers_t; -typedef enum { - TYPE_QUALIFIER_CONST = 1 << 0, - TYPE_QUALIFIER_RESTRICT = 1 << 1, - TYPE_QUALIFIER_VOLATILE = 1 << 2, - TYPE_QUALIFIER_INLINE = 1 << 3, -} type_qualifier_t; +#define STORAGE_CLASSES \ + case T_typedef: \ + case T_extern: \ + case T_static: \ + case T_auto: \ + case T_register: + +#define TYPE_QUALIFIERS \ + case T_const: \ + case T_restrict: \ + case T_volatile: \ + case T_inline: \ + case T___extension__: \ + case T___attribute__: -typedef enum { - STORAGE_CLASS_NONE, - STORAGE_CLASS_TYPEDEF, - STORAGE_CLASS_EXTERN, - STORAGE_CLASS_STATIC, - STORAGE_CLASS_AUTO, - STORAGE_CLASS_REGISTER -} storage_class_t; +#ifdef PROVIDE_COMPLEX +#define COMPLEX_SPECIFIERS \ + case T__Complex: +#else +#define COMPLEX_SPECIFIERS +#endif -typedef struct declaration_specifiers_t declaration_specifiers_t; -struct declaration_specifiers_t { - storage_class_t storage_class; - int type_qualifiers; -}; +#ifdef PROVIDE_IMAGINARY +#define IMAGINARY_SPECIFIERS \ + case T__Imaginary: +#else +#define IMAGINARY_SPECIFIERS +#endif + +#define TYPE_SPECIFIERS \ + case T_TYPENAME: \ + case T_void: \ + case T_char: \ + case T_short: \ + case T_int: \ + case T_long: \ + case T_float: \ + case T_double: \ + case T_signed: \ + case T_unsigned: \ + case T__Bool: \ + case T_struct: \ + case T_union: \ + case T_enum: \ + case T___quad_t: \ + case T___u_quad_t: \ + COMPLEX_SPECIFIERS \ + IMAGINARY_SPECIFIERS static void parse_declaration_specifiers(declaration_specifiers_t *specifiers) { - type_type_t type_type = TYPE_INVALID; - atomic_type_type_t atomic_type = ATOMIC_TYPE_INVALID; - unsigned type_specifiers = 0; + type_t *type = NULL; + unsigned type_qualifiers = 0; + unsigned type_specifiers = 0; while(1) { switch(token.type) { @@ -176,7 +384,7 @@ void parse_declaration_specifiers(declaration_specifiers_t *specifiers) /* type qualifiers */ #define MATCH_TYPE_QUALIFIER(token, qualifier) \ case token: \ - specifiers->type_qualifiers |= qualifier; \ + type_qualifiers |= qualifier; \ next_token(); \ break; @@ -185,6 +393,16 @@ void parse_declaration_specifiers(declaration_specifiers_t *specifiers) MATCH_TYPE_QUALIFIER(T_volatile, TYPE_QUALIFIER_VOLATILE); MATCH_TYPE_QUALIFIER(T_inline, TYPE_QUALIFIER_INLINE); + case T___extension__: + /* TODO */ + next_token(); + break; + + case T___attribute__: + fprintf(stderr, "TODO: __attribute__ not handled yet\n"); + next_token(); + break; + /* type specifiers */ #define MATCH_SPECIFIER(token, specifier, name) \ case token: \ @@ -214,7 +432,7 @@ void parse_declaration_specifiers(declaration_specifiers_t *specifiers) case T_long: next_token(); if(type_specifiers & SPECIFIER_LONG_LONG) { - parse_error("too many long type specifiers given"); + parse_error("multiple type specifiers given"); } else if(type_specifiers & SPECIFIER_LONG) { type_specifiers |= SPECIFIER_LONG_LONG; } else { @@ -222,19 +440,59 @@ void parse_declaration_specifiers(declaration_specifiers_t *specifiers) } break; + case T___quad_t: + next_token(); + if(type_specifiers & SPECIFIER_LONG_LONG || + type_specifiers & SPECIFIER_LONG) { + parse_error("multiple type specifiers given"); + } else { + type_specifiers |= SPECIFIER_LONG_LONG; + } + break; + + case T___u_quad_t: + next_token(); + if(type_specifiers & SPECIFIER_LONG_LONG || + type_specifiers & SPECIFIER_LONG || + type_specifiers & SPECIFIER_UNSIGNED) { + parse_error("multiple type specifiers given"); + } else { + type_specifiers |= SPECIFIER_LONG_LONG | SPECIFIER_UNSIGNED; + } + break; + case T_struct: + type = parse_struct_specifier(); + break; + case T_union: + type = parse_union_specifier(); + break; case T_enum: /* TODO */ assert(0); break; + case T_TYPENAME: + if(type != NULL || type_specifiers != 0) { + goto finish_specifiers; + } + + type = token.v.symbol->thing->declaration->type; + assert(type != NULL); + next_token(); + break; + /* function specifier */ default: - return;; + goto finish_specifiers; } } - if(type_type == TYPE_INVALID) { +finish_specifiers: + + if(type == NULL) { + atomic_type_type_t atomic_type; + /* match valid basic types */ switch(type_specifiers) { case SPECIFIER_VOID: @@ -334,43 +592,89 @@ void parse_declaration_specifiers(declaration_specifiers_t *specifiers) } else { parse_error("multiple datatypes in declaration"); } + atomic_type = ATOMIC_TYPE_INVALID; } + + atomic_type_t *atype = allocate_type_zero(sizeof(atype[0])); + atype->type.type = TYPE_ATOMIC; + atype->atype = atomic_type; + + type = (type_t*) atype; } else { if(type_specifiers != 0) { parse_error("multiple datatypes in declaration"); } } + + type->qualifiers = type_qualifiers; + + type_t *result = typehash_insert(type); + if(result != (type_t*) type) { + obstack_free(type_obst, type); + } + + specifiers->type = result; + + fprintf(stderr, "Specifiers type: "); + print_type(stderr, result); + fprintf(stderr, "\n"); } -typedef struct declarator_t declarator_t; -struct declarator_t { - /* pointer stuff... */ - symbol_t *symbol; +static +unsigned parse_type_qualifiers() +{ + unsigned type_qualifiers = 0; - declarator_t *next; -}; + while(1) { + switch(token.type) { + /* type qualifiers */ + MATCH_TYPE_QUALIFIER(T_const, TYPE_QUALIFIER_CONST); + MATCH_TYPE_QUALIFIER(T_restrict, TYPE_QUALIFIER_RESTRICT); + MATCH_TYPE_QUALIFIER(T_volatile, TYPE_QUALIFIER_VOLATILE); + MATCH_TYPE_QUALIFIER(T_inline, TYPE_QUALIFIER_INLINE); -declarator_t *parse_declarator(void) + default: + return type_qualifiers; + } + } +} + +static +void parse_declarator(declaration_t *declaration, storage_class_t storage_class, + type_t *type) { while(token.type == '*') { /* pointer */ next_token(); - //parse_type_qualifiers(); - } - declarator_t *declarator; + pointer_type_t *pointer_type + = allocate_type_zero(sizeof(pointer_type[0])); + pointer_type->type.type = TYPE_POINTER; + pointer_type->points_to = type; + + pointer_type->type.qualifiers = parse_type_qualifiers(); + + type_t *result = typehash_insert((type_t*) pointer_type); + if(result != (type_t*) pointer_type) { + obstack_free(type_obst, pointer_type); + } + + type = result; + } + declaration->storage_class = storage_class; + declaration->type = type; switch(token.type) { + case T_TYPENAME: case T_IDENTIFIER: - declarator = allocate_ast_zero(sizeof(declarator[0])); - declarator->symbol = token.v.symbol; + declaration->symbol = token.v.symbol; next_token(); - return declarator; + break; case '(': next_token(); - declarator = parse_declarator(); - expect(')') - return declarator; + parse_declarator(declaration, storage_class, type); + expect_void(')'); + break; default: parse_error("problem while parsing declarator"); } @@ -380,7 +684,7 @@ declarator_t *parse_declarator(void) /* parse parameter-type-list or identifier-list */ - expect(')'); + expect_void(')'); } else if(token.type == '[') { next_token(); @@ -388,28 +692,97 @@ declarator_t *parse_declarator(void) /* assignment_expression or '*' or nothing */ - expect(']'); + expect_void(']'); } - return declarator; + fprintf(stderr, "Declarator type: "); + print_type(stderr, type); + fprintf(stderr, "\n"); + + symbol_t *symbol = declaration->symbol; + + environment_entry_t *entry = environment_push(symbol); + entry->declaration = declaration; + entry->old_symbol_ID = symbol->ID; + + if(storage_class == STORAGE_CLASS_TYPEDEF) { + symbol->ID = T_TYPENAME; + } else { + symbol->ID = T_IDENTIFIER; + } } +static void parse_init_declarators(const declaration_specifiers_t *specifiers) { - (void) specifiers; - declarator_t *declarator = parse_declarator(); - if(token.type == '=') { + while(1) { + declaration_t *declaration = allocate_ast_zero(sizeof(declaration[0])); + + parse_declarator(declaration, specifiers->storage_class, + specifiers->type); + if(token.type == '=') { + next_token(); + // parse_initializer TODO + } else if(token.type == '{') { + parse_compound_statement(); + return; + } + + if(token.type != ',') + break; next_token(); - //parse_initialize(); } - (void) declarator; + expect_void(';'); } -typedef struct declaration_t declaration_t; -struct declaration_t { - declaration_specifiers_t specifiers; - declaration_t *declarators; -}; +static +void parse_struct_declarators(const declaration_specifiers_t *specifiers) +{ + while(1) { + declaration_t declaration; + compound_entry_t *entry = allocate_ast_zero(sizeof(entry[0])); + + if(token.type == ':') { + next_token(); + parse_constant_expression(); + /* TODO */ + } else { + parse_declarator(&declaration, specifiers->storage_class, + specifiers->type); + + if(token.type == ':') { + next_token(); + parse_constant_expression(); + /* TODO */ + } + } + + if(token.type != ',') + break; + next_token(); + } + expect_void(';'); +} + +static compound_entry_t *parse_compound_type_entries(void) +{ + eat('{'); + + compound_entry_t *entries = NULL; + + while(token.type != '}' && token.type != T_EOF) { + declaration_specifiers_t specifiers; + memset(&specifiers, 0, sizeof(specifiers)); + /* TODO not correct as this allows storage class stuff... but only + * specifiers and qualifiers sould be allowed here */ + parse_declaration_specifiers(&specifiers); + + parse_struct_declarators(&specifiers); + } + next_token(); + + return entries; +} void parse_declaration(void) { @@ -417,25 +790,15 @@ void parse_declaration(void) memset(&specifiers, 0, sizeof(specifiers)); parse_declaration_specifiers(&specifiers); + if(token.type == ';') { + next_token(); + return; + } parse_init_declarators(&specifiers); } -#if 0 -namespace_t *parse(FILE *in, const char *input_name) -{ - namespace_t *namespace = parse_namespace(); - - return namespace; -} -#endif - -static -expression_t *parse_sub_expression(unsigned precedence); -static -expression_t *parse_expression(void); - typedef expression_t* (*parse_expression_function) (unsigned precedence); typedef expression_t* (*parse_expression_infix_function) (unsigned precedence, expression_t *left); @@ -644,14 +1007,14 @@ expression_t *parse_##unexpression_type(unsigned precedence) \ return (expression_t*) unary_expression; \ } -CREATE_UNARY_EXPRESSION_PARSER('-', UNEXPR_NEGATE); -CREATE_UNARY_EXPRESSION_PARSER('+', UNEXPR_PLUS); -CREATE_UNARY_EXPRESSION_PARSER('!', UNEXPR_NOT); -CREATE_UNARY_EXPRESSION_PARSER('*', UNEXPR_DEREFERENCE); -CREATE_UNARY_EXPRESSION_PARSER('&', UNEXPR_TAKE_ADDRESS); -CREATE_UNARY_EXPRESSION_PARSER('~', UNEXPR_BITWISE_NEGATE); -CREATE_UNARY_EXPRESSION_PARSER(T_PLUSPLUS, UNEXPR_PREFIX_INCREMENT); -CREATE_UNARY_EXPRESSION_PARSER(T_MINUSMINUS, UNEXPR_PREFIX_DECREMENT); +CREATE_UNARY_EXPRESSION_PARSER('-', UNEXPR_NEGATE) +CREATE_UNARY_EXPRESSION_PARSER('+', UNEXPR_PLUS) +CREATE_UNARY_EXPRESSION_PARSER('!', UNEXPR_NOT) +CREATE_UNARY_EXPRESSION_PARSER('*', UNEXPR_DEREFERENCE) +CREATE_UNARY_EXPRESSION_PARSER('&', UNEXPR_TAKE_ADDRESS) +CREATE_UNARY_EXPRESSION_PARSER('~', UNEXPR_BITWISE_NEGATE) +CREATE_UNARY_EXPRESSION_PARSER(T_PLUSPLUS, UNEXPR_PREFIX_INCREMENT) +CREATE_UNARY_EXPRESSION_PARSER(T_MINUSMINUS, UNEXPR_PREFIX_DECREMENT) #define CREATE_UNARY_POSTFIX_EXPRESSION_PARSER(token_type, unexpression_type) \ static \ @@ -670,8 +1033,8 @@ expression_t *parse_##unexpression_type(unsigned precedence, \ return (expression_t*) unary_expression; \ } -CREATE_UNARY_POSTFIX_EXPRESSION_PARSER(T_PLUSPLUS, UNEXPR_POSTFIX_INCREMENT); -CREATE_UNARY_POSTFIX_EXPRESSION_PARSER(T_MINUSMINUS, UNEXPR_POSTFIX_DECREMENT); +CREATE_UNARY_POSTFIX_EXPRESSION_PARSER(T_PLUSPLUS, UNEXPR_POSTFIX_INCREMENT) +CREATE_UNARY_POSTFIX_EXPRESSION_PARSER(T_MINUSMINUS, UNEXPR_POSTFIX_DECREMENT) #define CREATE_BINEXPR_PARSER(token_type, binexpression_type) \ static \ @@ -692,22 +1055,22 @@ expression_t *parse_##binexpression_type(unsigned precedence, \ return (expression_t*) binexpr; \ } -CREATE_BINEXPR_PARSER('*', BINEXPR_MUL); -CREATE_BINEXPR_PARSER('/', BINEXPR_DIV); -CREATE_BINEXPR_PARSER('+', BINEXPR_ADD); -CREATE_BINEXPR_PARSER('-', BINEXPR_SUB); -CREATE_BINEXPR_PARSER('<', BINEXPR_LESS); -CREATE_BINEXPR_PARSER('>', BINEXPR_GREATER); -CREATE_BINEXPR_PARSER('=', BINEXPR_ASSIGN); -CREATE_BINEXPR_PARSER(T_EQUALEQUAL, BINEXPR_EQUAL); -CREATE_BINEXPR_PARSER(T_SLASHEQUAL, BINEXPR_NOTEQUAL); -CREATE_BINEXPR_PARSER(T_LESSEQUAL, BINEXPR_LESSEQUAL); -CREATE_BINEXPR_PARSER(T_GREATEREQUAL, BINEXPR_GREATEREQUAL); -CREATE_BINEXPR_PARSER('&', BINEXPR_BITWISE_AND); -CREATE_BINEXPR_PARSER('|', BINEXPR_BITWISE_OR); -CREATE_BINEXPR_PARSER('^', BINEXPR_BITWISE_XOR); -CREATE_BINEXPR_PARSER(T_LESSLESS, BINEXPR_SHIFTLEFT); -CREATE_BINEXPR_PARSER(T_GREATERGREATER, BINEXPR_SHIFTRIGHT); +CREATE_BINEXPR_PARSER('*', BINEXPR_MUL) +CREATE_BINEXPR_PARSER('/', BINEXPR_DIV) +CREATE_BINEXPR_PARSER('+', BINEXPR_ADD) +CREATE_BINEXPR_PARSER('-', BINEXPR_SUB) +CREATE_BINEXPR_PARSER('<', BINEXPR_LESS) +CREATE_BINEXPR_PARSER('>', BINEXPR_GREATER) +CREATE_BINEXPR_PARSER('=', BINEXPR_ASSIGN) +CREATE_BINEXPR_PARSER(T_EQUALEQUAL, BINEXPR_EQUAL) +CREATE_BINEXPR_PARSER(T_SLASHEQUAL, BINEXPR_NOTEQUAL) +CREATE_BINEXPR_PARSER(T_LESSEQUAL, BINEXPR_LESSEQUAL) +CREATE_BINEXPR_PARSER(T_GREATEREQUAL, BINEXPR_GREATEREQUAL) +CREATE_BINEXPR_PARSER('&', BINEXPR_BITWISE_AND) +CREATE_BINEXPR_PARSER('|', BINEXPR_BITWISE_OR) +CREATE_BINEXPR_PARSER('^', BINEXPR_BITWISE_XOR) +CREATE_BINEXPR_PARSER(T_LESSLESS, BINEXPR_SHIFTLEFT) +CREATE_BINEXPR_PARSER(T_GREATERGREATER, BINEXPR_SHIFTRIGHT) static expression_t *parse_sub_expression(unsigned precedence) @@ -834,12 +1197,6 @@ void init_expression_parsers(void) } -static -statement_t *parse_compound_statement(void); - -static -statement_t *parse_statement(void); - static statement_t *parse_case_statement(void) { @@ -986,6 +1343,13 @@ statement_t *parse_return(void) return NULL; } +static +statement_t *parse_declaration_statement(void) +{ + parse_declaration(); + return NULL; +} + static statement_t *parse_statement(void) { @@ -1048,6 +1412,12 @@ statement_t *parse_statement(void) case ';': statement = NULL; break; + + STORAGE_CLASSES + TYPE_QUALIFIERS + TYPE_SPECIFIERS + statement = parse_declaration_statement(); + break; } return statement; @@ -1056,46 +1426,50 @@ statement_t *parse_statement(void) static statement_t *parse_compound_statement(void) { - expect('{'); + eat('{'); + + int top = environment_top(); while(token.type != '}') { parse_statement(); } + + environment_pop_to(top); + next_token(); return NULL; } static -void parse_translation_unit(void) +translation_unit_t *parse_translation_unit(void) { - declaration_specifiers_t specifiers; - memset(&specifiers, 0, sizeof(specifiers)); - parse_declaration_specifiers(&specifiers); + translation_unit_t *unit = allocate_ast_zero(sizeof(unit[0])); - while(token.type != T_EOF) { - if(token.type == '{') { - next_token(); - continue; - } + assert(translation_unit == NULL); + assert(context == NULL); + translation_unit = unit; + while(token.type != T_EOF) { parse_declaration(); - /* multiple declarations? */ - - if(token.type == '{') { - parse_compound_statement(); - } else if(token.type == ';') { - next_token(); - } else { - parse_error_expected("while parsing declarations", '{', ';', 0); - } } + + translation_unit = NULL; + return unit; } -void parse(void) +translation_unit_t *parse(void) { + obstack_init(&environment_obstack); + environment_stack = NEW_ARR_F(environment_entry_t*, 0); + next_token(); - parse_translation_unit(); + translation_unit_t *unit = parse_translation_unit(); + + DEL_ARR_F(environment_stack); + obstack_free(&environment_obstack, NULL); + + return unit; } void init_parser(void)