aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-04-20 03:39:40 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-04-20 03:39:40 +0300
commit1cc7990ef7d5483d0434dda412f2d88e0b17df27 (patch)
tree93c9945318908c4f593251c0b8f9ecf57c3bde96
parentedf56e3444d5333d4362277ee97a5cdf0c2f52af (diff)
downloadposthaste-1cc7990ef7d5483d0434dda412f2d88e0b17df27.tar.gz
posthaste-1cc7990ef7d5483d0434dda412f2d88e0b17df27.zip
initial working bytecode
-rw-r--r--gen/gen_lexer.inc30
-rw-r--r--gen/gen_parser.c638
-rw-r--r--scripts/makefile4
-rw-r--r--src/ast.c225
-rw-r--r--src/check.c767
-rw-r--r--src/core.c60
-rw-r--r--src/date.c30
-rw-r--r--src/execute.c367
-rw-r--r--src/lexer.l10
-rw-r--r--src/lower.c730
-rw-r--r--src/parser.y239
-rw-r--r--src/scope.c153
-rw-r--r--src/vec.c75
13 files changed, 3025 insertions, 303 deletions
diff --git a/gen/gen_lexer.inc b/gen/gen_lexer.inc
index b6f9599..0e34d3f 100644
--- a/gen/gen_lexer.inc
+++ b/gen/gen_lexer.inc
@@ -1105,13 +1105,15 @@ case 36:
YY_RULE_SETUP
#line 156 "src/lexer.l"
{
- yylval->str = yytext;
+ /* skip quotation marks */
+ yylval->str = strdup(yytext + 1);
+ yylval->str[strlen(yylval->str) - 1] = '\0';
return STRING;
}
YY_BREAK
case 37:
YY_RULE_SETUP
-#line 161 "src/lexer.l"
+#line 163 "src/lexer.l"
{
yylval->num = lex_date(parser, src_loc(*yylloc), yytext);
return DATE_LITERAL;
@@ -1119,7 +1121,7 @@ YY_RULE_SETUP
YY_BREAK
case 38:
YY_RULE_SETUP
-#line 166 "src/lexer.l"
+#line 168 "src/lexer.l"
{
yylval->snum = lex_int(parser, src_loc(*yylloc), yytext);
return INT_LITERAL;
@@ -1127,47 +1129,47 @@ YY_RULE_SETUP
YY_BREAK
case 39:
YY_RULE_SETUP
-#line 171 "src/lexer.l"
+#line 173 "src/lexer.l"
{
- yylval->str = yytext;
+ yylval->str = strdup(yytext);
return IDENT;
}
YY_BREAK
case 40:
YY_RULE_SETUP
-#line 176 "src/lexer.l"
+#line 178 "src/lexer.l"
{
- yylval->str = yytext;
+ yylval->str = strdup(yytext);
return FUNC_IDENT;
}
YY_BREAK
case 41:
YY_RULE_SETUP
-#line 181 "src/lexer.l"
+#line 183 "src/lexer.l"
{
- yylval->str = yytext;
+ yylval->str = strdup(yytext);
return PROC_IDENT;
}
YY_BREAK
case 42:
/* rule 42 can match eol */
YY_RULE_SETUP
-#line 186 "src/lexer.l"
+#line 188 "src/lexer.l"
{/* skip whitespace */}
YY_BREAK
case 43:
YY_RULE_SETUP
-#line 188 "src/lexer.l"
+#line 190 "src/lexer.l"
{
lex_fail(parser, src_loc(*yylloc), "Unexpected token: %s", yytext);
}
YY_BREAK
case 44:
YY_RULE_SETUP
-#line 191 "src/lexer.l"
+#line 193 "src/lexer.l"
YY_FATAL_ERROR( "flex scanner jammed" );
YY_BREAK
-#line 1171 "gen/gen_lexer.inc"
+#line 1173 "gen/gen_lexer.inc"
case YY_STATE_EOF(INITIAL):
case YY_STATE_EOF(SC_COMMENT):
yyterminate();
@@ -2299,6 +2301,6 @@ void yyfree (void * ptr , yyscan_t yyscanner)
#define YYTABLES_NAME "yytables"
-#line 191 "src/lexer.l"
+#line 193 "src/lexer.l"
diff --git a/gen/gen_parser.c b/gen/gen_parser.c
index a1632fd..7d6e67d 100644
--- a/gen/gen_parser.c
+++ b/gen/gen_parser.c
@@ -77,6 +77,7 @@
#include <posthaste/parser.h>
#include <posthaste/date.h>
+#include <posthaste/ast.h>
#define FOREACH_TOKEN(M) \
M(LPAREN) \
@@ -115,7 +116,7 @@
M(PROC_IDENT)
-#line 119 "gen/gen_parser.c"
+#line 120 "gen/gen_parser.c"
# ifndef YY_CAST
# ifdef __cplusplus
@@ -198,14 +199,14 @@ extern int yydebug;
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
union YYSTYPE
{
-#line 59 "src/parser.y"
+#line 60 "src/parser.y"
- struct ast_node *node;
+ struct ast *node;
ph_date_t num;
int64_t snum;
char *str;
-#line 209 "gen/gen_parser.c"
+#line 210 "gen/gen_parser.c"
};
typedef union YYSTYPE YYSTYPE;
@@ -293,29 +294,30 @@ enum yysymbol_kind_t
YYSYMBOL_arguments = 52, /* arguments */
YYSYMBOL_opt_arguments = 53, /* opt_arguments */
YYSYMBOL_assignment = 54, /* assignment */
- YYSYMBOL_lvalue = 55, /* lvalue */
- YYSYMBOL_rvalue = 56, /* rvalue */
- YYSYMBOL_print_statement = 57, /* print_statement */
- YYSYMBOL_print_items = 58, /* print_items */
- YYSYMBOL_print_item = 59, /* print_item */
- YYSYMBOL_statement = 60, /* statement */
- YYSYMBOL_until_statement = 61, /* until_statement */
- YYSYMBOL_unless_statement = 62, /* unless_statement */
- YYSYMBOL_expression = 63, /* expression */
- YYSYMBOL_simple_expr = 64, /* simple_expr */
- YYSYMBOL_term = 65, /* term */
- YYSYMBOL_factor = 66, /* factor */
- YYSYMBOL_atom = 67, /* atom */
- YYSYMBOL_function_call = 68, /* function_call */
- YYSYMBOL_unless_expression = 69, /* unless_expression */
- YYSYMBOL_opt_definitions = 70, /* opt_definitions */
- YYSYMBOL_program = 71 /* program */
+ YYSYMBOL_ident = 55, /* ident */
+ YYSYMBOL_lvalue = 56, /* lvalue */
+ YYSYMBOL_rvalue = 57, /* rvalue */
+ YYSYMBOL_print_statement = 58, /* print_statement */
+ YYSYMBOL_print_items = 59, /* print_items */
+ YYSYMBOL_print_item = 60, /* print_item */
+ YYSYMBOL_statement = 61, /* statement */
+ YYSYMBOL_until_statement = 62, /* until_statement */
+ YYSYMBOL_unless_statement = 63, /* unless_statement */
+ YYSYMBOL_expression = 64, /* expression */
+ YYSYMBOL_simple_expr = 65, /* simple_expr */
+ YYSYMBOL_term = 66, /* term */
+ YYSYMBOL_factor = 67, /* factor */
+ YYSYMBOL_atom = 68, /* atom */
+ YYSYMBOL_function_call = 69, /* function_call */
+ YYSYMBOL_unless_expression = 70, /* unless_expression */
+ YYSYMBOL_opt_definitions = 71, /* opt_definitions */
+ YYSYMBOL_program = 72 /* program */
};
typedef enum yysymbol_kind_t yysymbol_kind_t;
/* Second part of user prologue. */
-#line 104 "src/parser.y"
+#line 143 "src/parser.y"
/** Modifies the signature of yylex to fit our parser better. */
@@ -355,7 +357,7 @@ static void yyerror(YYLTYPE *yylloc, void *lexer,
struct parser *parser, const char *msg);
-#line 359 "gen/gen_parser.c"
+#line 361 "gen/gen_parser.c"
#ifdef short
@@ -680,18 +682,18 @@ union yyalloc
#endif /* !YYCOPY_NEEDED */
/* YYFINAL -- State number of the termination state. */
-#define YYFINAL 29
+#define YYFINAL 30
/* YYLAST -- Last index in YYTABLE. */
#define YYLAST 156
/* YYNTOKENS -- Number of terminals. */
#define YYNTOKENS 37
/* YYNNTS -- Number of nonterminals. */
-#define YYNNTS 35
+#define YYNNTS 36
/* YYNRULES -- Number of rules. */
-#define YYNRULES 72
+#define YYNRULES 73
/* YYNSTATES -- Number of states. */
-#define YYNSTATES 136
+#define YYNSTATES 137
/* YYMAXUTOK -- Last valid token kind. */
#define YYMAXUTOK 291
@@ -744,14 +746,14 @@ static const yytype_int8 yytranslate[] =
/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
static const yytype_int16 yyrline[] =
{
- 0, 148, 148, 149, 152, 153, 156, 157, 158, 161,
- 164, 165, 168, 169, 172, 175, 176, 179, 183, 188,
- 189, 192, 193, 196, 199, 202, 203, 206, 207, 210,
- 213, 214, 217, 218, 221, 224, 225, 228, 229, 232,
- 233, 234, 235, 236, 237, 240, 243, 244, 247, 248,
- 249, 252, 253, 254, 257, 258, 259, 262, 263, 264,
- 267, 268, 269, 270, 271, 272, 273, 276, 279, 282,
- 283, 286, 287
+ 0, 187, 187, 188, 191, 192, 195, 196, 197, 200,
+ 205, 206, 209, 210, 213, 216, 217, 220, 232, 244,
+ 245, 248, 249, 252, 257, 262, 263, 266, 267, 270,
+ 275, 280, 281, 284, 285, 288, 291, 292, 295, 296,
+ 299, 300, 301, 302, 303, 304, 307, 312, 315, 320,
+ 321, 324, 329, 330, 333, 338, 339, 342, 347, 350,
+ 353, 356, 357, 360, 363, 366, 367, 368, 371, 376,
+ 381, 382, 385, 396
};
#endif
@@ -778,10 +780,11 @@ static const char *const yytname[] =
"opt_variable_definitions", "return", "opt_return",
"function_definition", "procedure_definition", "formals", "opt_formals",
"formal_arg", "procedure_call", "arguments", "opt_arguments",
- "assignment", "lvalue", "rvalue", "print_statement", "print_items",
- "print_item", "statement", "until_statement", "unless_statement",
- "expression", "simple_expr", "term", "factor", "atom", "function_call",
- "unless_expression", "opt_definitions", "program", YY_NULLPTR
+ "assignment", "ident", "lvalue", "rvalue", "print_statement",
+ "print_items", "print_item", "statement", "until_statement",
+ "unless_statement", "expression", "simple_expr", "term", "factor",
+ "atom", "function_call", "unless_expression", "opt_definitions",
+ "program", YY_NULLPTR
};
static const char *
@@ -791,34 +794,34 @@ yysymbol_name (yysymbol_kind_t yysymbol)
}
#endif
-#define YYPACT_NINF (-21)
+#define YYPACT_NINF (-18)
#define yypact_value_is_default(Yyn) \
((Yyn) == YYPACT_NINF)
-#define YYTABLE_NINF (-71)
+#define YYTABLE_NINF (-72)
#define yytable_value_is_error(Yyn) \
0
/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
STATE-NUM. */
-static const yytype_int8 yypact[] =
+static const yytype_int16 yypact[] =
{
- 5, -21, 4, 51, 56, -21, 32, -21, -21, -21,
- -3, 83, 81, 89, 95, -21, 97, 104, -3, 66,
- 47, -21, -21, -21, 98, -21, 99, -21, -21, -21,
- 66, 112, 112, 113, 66, -20, -21, -21, 105, 114,
- 66, 86, 86, -21, 9, 3, 20, -21, -21, -21,
- -21, -21, 106, 9, 12, -3, 9, 110, -21, 111,
- 107, 115, -21, -21, 116, 65, 66, 66, 121, 66,
- 57, -21, -21, 66, 66, 66, 66, 66, 66, 47,
- 66, -21, 9, -21, -21, 122, 96, 112, 96, -21,
- 66, -5, 9, -21, 123, -21, 3, 3, 20, 20,
- -21, -21, -21, 59, 119, 126, -21, 109, -21, 109,
- -21, -3, -21, -21, 66, -21, -21, 109, -21, 117,
- 118, 108, 61, -21, -3, 12, -21, 66, 100, 101,
- 27, 103, 120, -21, -21, -21
+ 23, -18, 20, 11, 14, -18, -15, -18, -18, -18,
+ -2, 30, 21, 31, 41, -18, -18, 51, -2, 113,
+ 68, -18, -18, -18, 70, 89, -18, 93, -18, -18,
+ -18, 113, 107, 107, 113, 34, -18, -18, 105, 113,
+ 88, 88, -18, 108, 59, 63, 57, -18, -18, -18,
+ -18, -18, 109, 59, 118, 61, -2, 59, 115, -18,
+ 114, 110, 116, -18, 119, 22, 113, 113, 113, 15,
+ -18, -18, 125, 113, 113, 113, 113, 113, 113, 68,
+ -18, 113, -18, 59, -18, -18, 126, 99, 107, 99,
+ -18, 113, -8, 59, 127, -18, -18, 63, 63, 57,
+ 57, -18, -18, -18, 32, 124, 132, -18, 117, -18,
+ 117, -18, -2, -18, -18, 113, -18, -18, 117, -18,
+ 120, 121, 112, 17, -18, -2, 61, -18, 113, 103,
+ 104, -4, 122, 111, -18, -18, -18
};
/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
@@ -826,38 +829,38 @@ static const yytype_int8 yypact[] =
means the default is an error. */
static const yytype_int8 yydefact[] =
{
- 0, 72, 0, 0, 0, 69, 5, 8, 6, 7,
+ 0, 73, 0, 0, 0, 70, 5, 8, 6, 7,
0, 0, 0, 0, 0, 4, 30, 0, 0, 0,
- 0, 71, 39, 40, 0, 41, 3, 42, 43, 1,
- 0, 22, 22, 0, 28, 0, 63, 62, 60, 0,
- 0, 0, 0, 65, 44, 48, 51, 54, 59, 64,
- 37, 34, 36, 38, 0, 0, 9, 0, 21, 0,
- 20, 0, 31, 27, 0, 26, 0, 0, 0, 28,
- 0, 57, 58, 0, 0, 0, 0, 0, 0, 0,
- 0, 29, 32, 33, 2, 0, 16, 0, 0, 24,
- 0, 0, 45, 61, 0, 66, 49, 50, 52, 53,
- 55, 56, 35, 0, 0, 0, 15, 13, 19, 13,
- 25, 0, 46, 67, 0, 23, 14, 11, 12, 0,
- 0, 0, 0, 10, 0, 0, 47, 0, 0, 0,
- 0, 0, 0, 68, 18, 17
+ 0, 72, 40, 41, 31, 0, 42, 3, 43, 44,
+ 1, 0, 22, 22, 28, 0, 64, 63, 0, 0,
+ 0, 0, 66, 61, 45, 49, 52, 55, 60, 65,
+ 38, 35, 37, 39, 0, 0, 0, 9, 0, 21,
+ 0, 20, 0, 27, 0, 26, 0, 0, 28, 0,
+ 58, 59, 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 0, 29, 33, 34, 2, 0, 16, 0, 0,
+ 24, 0, 0, 46, 0, 67, 62, 50, 51, 53,
+ 54, 56, 57, 36, 0, 0, 0, 15, 13, 19,
+ 13, 25, 0, 47, 68, 0, 23, 14, 11, 12,
+ 0, 0, 0, 0, 10, 0, 0, 48, 0, 0,
+ 0, 0, 0, 0, 69, 18, 17
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int16 yypgoto[] =
{
- -21, -14, 132, -21, 60, 23, 33, 53, -21, -21,
- -21, -21, 124, 58, -10, 62, 77, -21, -21, 22,
- -21, 69, -21, -21, -21, -21, -18, 25, 28, 34,
- 64, -21, -21, -21, -21
+ -18, -13, 135, -18, 7, 33, 35, 60, -18, -18,
+ -18, 62, 123, -18, -10, 64, 80, -18, -9, -18,
+ 26, -18, 74, -18, -18, -18, -18, -17, 13, 25,
+ 27, 66, -18, -18, -18, -18
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int8 yydefgoto[] =
{
- 0, 21, 5, 6, 117, 118, 119, 106, 107, 8,
- 9, 58, 59, 60, 43, 63, 64, 23, 24, 81,
- 25, 51, 52, 26, 27, 28, 65, 45, 46, 47,
- 48, 49, 83, 10, 11
+ 0, 21, 5, 6, 118, 119, 120, 107, 108, 8,
+ 9, 59, 60, 61, 42, 63, 64, 23, 43, 25,
+ 82, 26, 51, 52, 27, 28, 29, 65, 45, 46,
+ 47, 48, 49, 84, 10, 11
};
/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
@@ -865,42 +868,42 @@ static const yytype_int8 yydefgoto[] =
number is the opposite. If YYTABLE_NINF, syntax error. */
static const yytype_int16 yytable[] =
{
- 22, 44, 53, 16, 35, 17, 1, 66, 22, 67,
- 12, -70, 56, -70, 73, 74, 36, 37, 38, 39,
- 17, 40, 70, 111, 75, 76, 112, 18, 73, 74,
- 2, 19, 20, 41, 42, -70, 82, 3, 4, -70,
- -70, 84, 80, 77, 78, 22, 73, 74, 91, 92,
- 50, 36, 37, 38, 39, 17, 40, 2, 133, 13,
- 7, 53, 103, 14, 3, 4, 7, 95, 41, 42,
- 36, 37, 38, 39, 17, 40, 73, 74, 73, 74,
- 73, 74, 90, 29, 73, 74, 114, 41, 42, 127,
- 36, 37, 38, 39, 17, 40, 122, 121, 96, 97,
- 30, 22, 31, 98, 99, 71, 72, 82, 32, 130,
- 128, 100, 101, 34, 22, 33, 55, 54, 57, 62,
- 68, 85, 79, 69, 87, 86, 89, 93, 104, 88,
- 105, 115, 116, 113, 2, 134, 131, 132, 15, 126,
- 123, 109, 120, 124, 125, 108, 94, 129, 102, 0,
- 0, 0, 110, 135, 0, 0, 61
+ 22, 24, 44, 53, 16, 35, 17, 7, 22, 24,
+ 2, 73, 74, 7, 57, 73, 74, 3, 4, 13,
+ 112, 14, 69, 113, 1, 95, 12, 134, 18, -71,
+ 30, -71, 19, 20, 73, 74, 73, 74, 83, 91,
+ 31, 73, 74, 85, 32, 128, 22, 24, 2, 92,
+ 93, 73, 74, -71, 33, 3, 4, -71, -71, 115,
+ 34, 66, 53, 67, 104, 36, 37, 16, 38, 17,
+ 39, 50, 36, 37, 16, 38, 17, 39, 73, 74,
+ 77, 78, 40, 41, 75, 76, 97, 98, 54, 40,
+ 41, 81, 36, 37, 16, 38, 17, 39, 123, 122,
+ 99, 100, 22, 24, 101, 102, 70, 71, 55, 83,
+ 56, 131, 129, 58, 68, 22, 24, 36, 37, 16,
+ 38, 17, 39, 72, 80, 79, 86, 88, 87, 90,
+ 89, 96, 105, 106, 40, 41, 116, 114, 117, 132,
+ 133, 15, 2, 127, 136, 121, 125, 126, 94, 110,
+ 109, 124, 130, 103, 135, 111, 62
};
-static const yytype_int8 yycheck[] =
+static const yytype_uint8 yycheck[] =
{
- 10, 19, 20, 6, 18, 8, 1, 27, 18, 29,
- 6, 6, 30, 8, 19, 20, 4, 5, 6, 7,
- 8, 9, 40, 28, 21, 22, 31, 30, 19, 20,
- 25, 34, 35, 21, 22, 30, 54, 32, 33, 34,
- 35, 55, 30, 23, 24, 55, 19, 20, 66, 67,
- 3, 4, 5, 6, 7, 8, 9, 25, 31, 8,
- 0, 79, 80, 7, 32, 33, 6, 10, 21, 22,
- 4, 5, 6, 7, 8, 9, 19, 20, 19, 20,
- 19, 20, 17, 0, 19, 20, 27, 21, 22, 28,
- 4, 5, 6, 7, 8, 9, 114, 111, 73, 74,
- 19, 111, 13, 75, 76, 41, 42, 125, 13, 127,
- 124, 77, 78, 9, 124, 18, 17, 19, 6, 6,
- 15, 11, 16, 9, 17, 14, 10, 6, 6, 14,
- 34, 12, 6, 10, 25, 32, 36, 36, 6, 31,
- 117, 88, 109, 26, 26, 87, 69, 125, 79, -1,
- -1, -1, 90, 33, -1, -1, 32
+ 10, 10, 19, 20, 6, 18, 8, 0, 18, 18,
+ 25, 19, 20, 6, 31, 19, 20, 32, 33, 8,
+ 28, 7, 39, 31, 1, 10, 6, 31, 30, 6,
+ 0, 8, 34, 35, 19, 20, 19, 20, 55, 17,
+ 19, 19, 20, 56, 13, 28, 56, 56, 25, 66,
+ 67, 19, 20, 30, 13, 32, 33, 34, 35, 27,
+ 9, 27, 79, 29, 81, 4, 5, 6, 7, 8,
+ 9, 3, 4, 5, 6, 7, 8, 9, 19, 20,
+ 23, 24, 21, 22, 21, 22, 73, 74, 18, 21,
+ 22, 30, 4, 5, 6, 7, 8, 9, 115, 112,
+ 75, 76, 112, 112, 77, 78, 40, 41, 19, 126,
+ 17, 128, 125, 6, 9, 125, 125, 4, 5, 6,
+ 7, 8, 9, 15, 6, 16, 11, 17, 14, 10,
+ 14, 6, 6, 34, 21, 22, 12, 10, 6, 36,
+ 36, 6, 25, 31, 33, 110, 26, 26, 68, 89,
+ 88, 118, 126, 79, 32, 91, 33
};
/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
@@ -908,19 +911,19 @@ static const yytype_int8 yycheck[] =
static const yytype_int8 yystos[] =
{
0, 1, 25, 32, 33, 39, 40, 41, 46, 47,
- 70, 71, 6, 8, 7, 39, 6, 8, 30, 34,
- 35, 38, 51, 54, 55, 57, 60, 61, 62, 0,
- 19, 13, 13, 18, 9, 38, 4, 5, 6, 7,
- 9, 21, 22, 51, 63, 64, 65, 66, 67, 68,
- 3, 58, 59, 63, 19, 17, 63, 6, 48, 49,
- 50, 49, 6, 52, 53, 63, 27, 29, 15, 9,
- 63, 67, 67, 19, 20, 21, 22, 23, 24, 16,
- 30, 56, 63, 69, 38, 11, 14, 17, 14, 10,
- 17, 63, 63, 6, 53, 10, 64, 64, 65, 65,
- 66, 66, 58, 63, 6, 34, 44, 45, 50, 44,
- 52, 28, 31, 10, 27, 12, 6, 41, 42, 43,
- 43, 38, 63, 42, 26, 26, 31, 28, 38, 56,
- 63, 36, 36, 31, 32, 33
+ 71, 72, 6, 8, 7, 39, 6, 8, 30, 34,
+ 35, 38, 51, 54, 55, 56, 58, 61, 62, 63,
+ 0, 19, 13, 13, 9, 38, 4, 5, 7, 9,
+ 21, 22, 51, 55, 64, 65, 66, 67, 68, 69,
+ 3, 59, 60, 64, 18, 19, 17, 64, 6, 48,
+ 49, 50, 49, 52, 53, 64, 27, 29, 9, 64,
+ 68, 68, 15, 19, 20, 21, 22, 23, 24, 16,
+ 6, 30, 57, 64, 70, 38, 11, 14, 17, 14,
+ 10, 17, 64, 64, 53, 10, 6, 65, 65, 66,
+ 66, 67, 67, 59, 64, 6, 34, 44, 45, 48,
+ 44, 52, 28, 31, 10, 27, 12, 6, 41, 42,
+ 43, 43, 38, 64, 42, 26, 26, 31, 28, 38,
+ 57, 64, 36, 36, 31, 32, 33
};
/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */
@@ -929,11 +932,11 @@ static const yytype_int8 yyr1[] =
0, 37, 38, 38, 39, 39, 40, 40, 40, 41,
42, 42, 43, 43, 44, 45, 45, 46, 47, 48,
48, 49, 49, 50, 51, 52, 52, 53, 53, 54,
- 55, 55, 56, 56, 57, 58, 58, 59, 59, 60,
- 60, 60, 60, 60, 60, 61, 62, 62, 63, 63,
- 63, 64, 64, 64, 65, 65, 65, 66, 66, 66,
- 67, 67, 67, 67, 67, 67, 67, 68, 69, 70,
- 70, 71, 71
+ 55, 56, 56, 57, 57, 58, 59, 59, 60, 60,
+ 61, 61, 61, 61, 61, 61, 62, 63, 63, 64,
+ 64, 64, 65, 65, 65, 66, 66, 66, 67, 67,
+ 67, 68, 68, 68, 68, 68, 68, 68, 69, 70,
+ 71, 71, 72, 72
};
/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */
@@ -942,11 +945,11 @@ static const yytype_int8 yyr2[] =
0, 2, 3, 1, 2, 1, 1, 1, 1, 4,
2, 1, 1, 0, 2, 1, 0, 11, 11, 3,
1, 1, 0, 4, 4, 3, 1, 1, 0, 3,
- 1, 3, 1, 1, 2, 3, 1, 1, 1, 1,
- 1, 1, 1, 1, 2, 4, 5, 7, 1, 3,
- 3, 1, 3, 3, 1, 3, 3, 2, 2, 1,
- 1, 3, 1, 1, 1, 1, 3, 4, 7, 1,
- 0, 2, 1
+ 1, 1, 3, 1, 1, 2, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 4, 5, 7, 1,
+ 3, 3, 1, 3, 3, 1, 3, 3, 2, 2,
+ 1, 1, 3, 1, 1, 1, 1, 3, 4, 7,
+ 1, 0, 2, 1
};
@@ -1805,23 +1808,330 @@ yyreduce:
YY_REDUCE_PRINT (yyn);
switch (yyn)
{
- case 70: /* opt_definitions: %empty */
-#line 283 "src/parser.y"
- {}
-#line 1812 "gen/gen_parser.c"
+ case 2: /* statement_list: statement "," statement_list */
+#line 187 "src/parser.y"
+ { (yyval.node) = (yyvsp[-2].node); (yyval.node)->n = (yyvsp[0].node); }
+#line 1815 "gen/gen_parser.c"
break;
- case 72: /* program: error */
-#line 287 "src/parser.y"
+ case 4: /* definitions: definition definitions */
+#line 191 "src/parser.y"
+ { (yyval.node) = (yyvsp[-1].node); (yyval.node)->n = (yyvsp[0].node); }
+#line 1821 "gen/gen_parser.c"
+ break;
+
+ case 9: /* variable_definition: "var" IDENT "=" expression */
+#line 200 "src/parser.y"
+ {
+ (yyval.node) = gen_var_def((yyvsp[-2].str), (yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 1829 "gen/gen_parser.c"
+ break;
+
+ case 10: /* variable_definitions: variable_definition variable_definitions */
+#line 205 "src/parser.y"
+ { (yyval.node) = (yyvsp[-1].node); (yyval.node)->n = (yyvsp[0].node); }
+#line 1835 "gen/gen_parser.c"
+ break;
+
+ case 13: /* opt_variable_definitions: %empty */
+#line 210 "src/parser.y"
+ { (yyval.node) = NULL; }
+#line 1841 "gen/gen_parser.c"
+ break;
+
+ case 14: /* return: "return" IDENT */
+#line 213 "src/parser.y"
+ { (yyval.str) = (yyvsp[0].str); }
+#line 1847 "gen/gen_parser.c"
+ break;
+
+ case 16: /* opt_return: %empty */
+#line 217 "src/parser.y"
+ { (yyval.str) = NULL; }
+#line 1853 "gen/gen_parser.c"
+ break;
+
+ case 17: /* function_definition: "function" FUNC_IDENT "{" opt_formals "}" return opt_variable_definitions "is" rvalue "end" "function" */
+#line 221 "src/parser.y"
+ {
+ struct ast *ret = gen_return((yyvsp[-2].node), (yyvsp[-2].node)->loc);
+ (yyval.node) = gen_func_def((yyvsp[-9].str),
+ (yyvsp[-5].str),
+ (yyvsp[-7].node),
+ (yyvsp[-4].node),
+ ret,
+ src_loc((yyloc)));
+ }
+#line 1867 "gen/gen_parser.c"
+ break;
+
+ case 18: /* procedure_definition: "procedure" PROC_IDENT "{" opt_formals "}" opt_return opt_variable_definitions "is" statement_list "end" "procedure" */
+#line 234 "src/parser.y"
+ {
+ (yyval.node) = gen_proc_def((yyvsp[-9].str),
+ (yyvsp[-5].str),
+ (yyvsp[-7].node),
+ (yyvsp[-4].node),
+ (yyvsp[-2].node),
+ src_loc((yyloc)));
+ }
+#line 1880 "gen/gen_parser.c"
+ break;
+
+ case 19: /* formals: formal_arg "," formals */
+#line 244 "src/parser.y"
+ { (yyval.node) = (yyvsp[-2].node); (yyval.node)->n = (yyvsp[0].node); }
+#line 1886 "gen/gen_parser.c"
+ break;
+
+ case 22: /* opt_formals: %empty */
+#line 249 "src/parser.y"
+ { (yyval.node) = NULL; }
+#line 1892 "gen/gen_parser.c"
+ break;
+
+ case 23: /* formal_arg: IDENT "[" IDENT "]" */
+#line 252 "src/parser.y"
+ {
+ (yyval.node) = gen_formal_def((yyvsp[-3].str), (yyvsp[-1].str), src_loc((yyloc)));
+ }
+#line 1900 "gen/gen_parser.c"
+ break;
+
+ case 24: /* procedure_call: PROC_IDENT "(" opt_arguments ")" */
+#line 257 "src/parser.y"
+ {
+ (yyval.node) = gen_proc_call((yyvsp[-3].str), (yyvsp[-1].node), src_loc((yyloc)));
+ }
+#line 1908 "gen/gen_parser.c"
+ break;
+
+ case 25: /* arguments: expression "," arguments */
+#line 262 "src/parser.y"
+ { (yyval.node) = (yyvsp[-2].node); (yyval.node)->n = (yyvsp[0].node); }
+#line 1914 "gen/gen_parser.c"
+ break;
+
+ case 28: /* opt_arguments: %empty */
+#line 267 "src/parser.y"
+ { (yyval.node) = NULL; }
+#line 1920 "gen/gen_parser.c"
+ break;
+
+ case 29: /* assignment: lvalue "=" rvalue */
+#line 270 "src/parser.y"
+ {
+ (yyval.node) = gen_assign((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 1928 "gen/gen_parser.c"
+ break;
+
+ case 30: /* ident: IDENT */
+#line 275 "src/parser.y"
+ {
+ (yyval.node) = gen_id((yyvsp[0].str), src_loc((yyloc)));
+ }
+#line 1936 "gen/gen_parser.c"
+ break;
+
+ case 32: /* lvalue: ident "." IDENT */
+#line 281 "src/parser.y"
+ { (yyval.node) = gen_dot((yyvsp[-2].node), (yyvsp[0].str), src_loc((yyloc))); }
+#line 1942 "gen/gen_parser.c"
+ break;
+
+ case 35: /* print_statement: "print" print_items */
+#line 288 "src/parser.y"
+ { (yyval.node) = gen_print((yyvsp[0].node), src_loc((yyloc))); }
+#line 1948 "gen/gen_parser.c"
+ break;
+
+ case 36: /* print_items: print_item "&" print_items */
+#line 291 "src/parser.y"
+ { (yyval.node) = (yyvsp[-2].node); (yyval.node)->n = (yyvsp[0].node); }
+#line 1954 "gen/gen_parser.c"
+ break;
+
+ case 38: /* print_item: STRING */
+#line 295 "src/parser.y"
+ { (yyval.node) = gen_const_string((yyvsp[0].str), src_loc((yyloc))); }
+#line 1960 "gen/gen_parser.c"
+ break;
+
+ case 45: /* statement: "return" expression */
+#line 304 "src/parser.y"
+ { (yyval.node) = gen_return((yyvsp[0].node), src_loc((yyloc))); }
+#line 1966 "gen/gen_parser.c"
+ break;
+
+ case 46: /* until_statement: "do" statement_list "until" expression */
+#line 307 "src/parser.y"
+ {
+ (yyval.node) = gen_until((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 1974 "gen/gen_parser.c"
+ break;
+
+ case 47: /* unless_statement: "do" statement_list "unless" expression "done" */
+#line 312 "src/parser.y"
+ {
+ (yyval.node) = gen_unless((yyvsp[-3].node), (yyvsp[-1].node), NULL, src_loc((yyloc)));
+ }
+#line 1982 "gen/gen_parser.c"
+ break;
+
+ case 48: /* unless_statement: "do" statement_list "unless" expression "otherwise" statement_list "done" */
+#line 315 "src/parser.y"
+ {
+ (yyval.node) = gen_unless((yyvsp[-5].node), (yyvsp[-3].node), (yyvsp[-1].node), src_loc((yyloc)));
+ }
+#line 1990 "gen/gen_parser.c"
+ break;
+
+ case 50: /* expression: expression "=" simple_expr */
+#line 321 "src/parser.y"
+ {
+ (yyval.node) = gen_eq((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 1998 "gen/gen_parser.c"
+ break;
+
+ case 51: /* expression: expression "<" simple_expr */
+#line 324 "src/parser.y"
+ {
+ (yyval.node) = gen_lt((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 2006 "gen/gen_parser.c"
+ break;
+
+ case 53: /* simple_expr: simple_expr "+" term */
+#line 330 "src/parser.y"
+ {
+ (yyval.node) = gen_add((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 2014 "gen/gen_parser.c"
+ break;
+
+ case 54: /* simple_expr: simple_expr "-" term */
+#line 333 "src/parser.y"
+ {
+ (yyval.node) = gen_sub((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 2022 "gen/gen_parser.c"
+ break;
+
+ case 56: /* term: term "*" factor */
+#line 339 "src/parser.y"
+ {
+ (yyval.node) = gen_mul((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 2030 "gen/gen_parser.c"
+ break;
+
+ case 57: /* term: term "/" factor */
+#line 342 "src/parser.y"
+ {
+ (yyval.node) = gen_div((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 2038 "gen/gen_parser.c"
+ break;
+
+ case 58: /* factor: "+" atom */
+#line 347 "src/parser.y"
+ {
+ (yyval.node) = gen_pos((yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 2046 "gen/gen_parser.c"
+ break;
+
+ case 59: /* factor: "-" atom */
+#line 350 "src/parser.y"
+ {
+ (yyval.node) = gen_neg((yyvsp[0].node), src_loc((yyloc)));
+ }
+#line 2054 "gen/gen_parser.c"
+ break;
+
+ case 62: /* atom: ident "'" IDENT */
+#line 357 "src/parser.y"
+ {
+ (yyval.node) = gen_attr((yyvsp[-2].node), (yyvsp[0].str), src_loc((yyloc)));
+ }
+#line 2062 "gen/gen_parser.c"
+ break;
+
+ case 63: /* atom: INT_LITERAL */
+#line 360 "src/parser.y"
+ {
+ (yyval.node) = gen_const_int((yyvsp[0].snum), src_loc((yyloc)));
+ }
+#line 2070 "gen/gen_parser.c"
+ break;
+
+ case 64: /* atom: DATE_LITERAL */
+#line 363 "src/parser.y"
+ {
+ (yyval.node) = gen_const_date((yyvsp[0].num), src_loc((yyloc)));
+ }
+#line 2078 "gen/gen_parser.c"
+ break;
+
+ case 67: /* atom: "(" expression ")" */
+#line 368 "src/parser.y"
+ { (yyval.node) = (yyvsp[-1].node); }
+#line 2084 "gen/gen_parser.c"
+ break;
+
+ case 68: /* function_call: FUNC_IDENT "(" opt_arguments ")" */
+#line 371 "src/parser.y"
+ {
+ (yyval.node) = gen_func_call((yyvsp[-3].str), (yyvsp[-1].node), src_loc((yyloc)));
+ }
+#line 2092 "gen/gen_parser.c"
+ break;
+
+ case 69: /* unless_expression: "do" expression "unless" expression "otherwise" expression "done" */
+#line 376 "src/parser.y"
+ {
+ (yyval.node) = gen_unless_expr((yyvsp[-5].node), (yyvsp[-3].node), (yyvsp[-1].node), src_loc((yyloc)));
+ }
+#line 2100 "gen/gen_parser.c"
+ break;
+
+ case 71: /* opt_definitions: %empty */
+#line 382 "src/parser.y"
+ { (yyval.node) = NULL; }
+#line 2106 "gen/gen_parser.c"
+ break;
+
+ case 72: /* program: opt_definitions statement_list */
+#line 385 "src/parser.y"
+ {
+ if ((yyvsp[-1].node)) {
+ ast_last((yyvsp[-1].node))->n = (yyvsp[0].node);
+ (yyval.node) = (yyvsp[-1].node);
+ }
+ else {
+ (yyval.node) = (yyvsp[0].node);
+ }
+
+ parser->tree = (yyval.node);
+ }
+#line 2122 "gen/gen_parser.c"
+ break;
+
+ case 73: /* program: error */
+#line 396 "src/parser.y"
{
/* make sure to catch parse errors */
parser->failed = true;
}
-#line 1821 "gen/gen_parser.c"
+#line 2131 "gen/gen_parser.c"
break;
-#line 1825 "gen/gen_parser.c"
+#line 2135 "gen/gen_parser.c"
default: break;
}
@@ -2050,49 +2360,11 @@ yyreturnlab:
return yyresult;
}
-#line 292 "src/parser.y"
+#line 401 "src/parser.y"
#include "gen_lexer.inc"
-static void dump_yychar(struct parser *p, int yychar, YYSTYPE yylval, YYLTYPE yylloc)
-{
- struct src_loc loc = src_loc(yylloc);
- printf("%s:%d:%d: ", p->fname, loc.first_line, loc.first_col);
-
-#define PRINT_NAME(token) case token: printf(#token " "); break;
- switch (yychar) {
- FOREACH_TOKEN(PRINT_NAME);
- default: printf("Unknown yychar\n"); return;
- }
-
- char date_str[11] = {0};
- switch (yychar) {
- case INT_LITERAL: printf("(%lld)", (long long int)yylval.snum); break;
- case IDENT: printf("(%s)", yylval.str); break;
- case FUNC_IDENT: printf("(%s)", yylval.str); break;
- case PROC_IDENT: printf("(%s)", yylval.str); break;
- case DATE_LITERAL:
- date_to_string(date_str, yylval.num);
- printf("(%s)", date_str);
- break;
- }
-
- printf("\n");
-}
-
-static void dump_lex(struct parser *p)
-{
- int yychar;
- YYSTYPE yylval;
- YYLTYPE yylloc = {1, 1, 1, 1};
-
- /* run lexer until we reach the end of the file */
- while ((yychar = yylex(&yylval, &yylloc, p->lexer, p)) != YYEOF) {
- dump_yychar(p, yychar, yylval, yylloc);
- }
-}
-
static struct src_loc src_loc(YYLTYPE yylloc)
{
struct src_loc loc;
@@ -2139,11 +2411,5 @@ void parse(struct parser *p, const char *fname, const char *buf)
yylex_init(&p->lexer);
yy_scan_string(buf, p->lexer);
-
- // debugging, remember to reset yy_scan_string once the actual parser
- // runs
- //dump_lex(p);
-
- yy_scan_string(buf, p->lexer);
yyparse(p->lexer, p);
}
diff --git a/scripts/makefile b/scripts/makefile
index fc1eb58..cf8a07e 100644
--- a/scripts/makefile
+++ b/scripts/makefile
@@ -29,7 +29,7 @@ WARNFLAGS = -Wall -Wextra -Wvla
COMPILE_FLAGS = $(CFLAGS) $(WARNFLAGS) $(OPTFLAGS) $(OBFLAGS) $(ASSERTFLAGS) \
$(DEBUGFLAGS)
-LINK_FLAGS = $(LDFLAGS)
+LINK_FLAGS = $(LDFLAGS) -lm
INCLUDE_FLAGS = -I include
@@ -50,7 +50,7 @@ gen/gen_lexer.inc: src/lexer.l
flex -o gen/gen_lexer.inc src/lexer.l
posthaste: $(POSTHASTE_OBJS)
- $(COMPILE_POSTHASTE) $(POSTHASTE_OBJS) -o $@
+ $(COMPILE_POSTHASTE) $(POSTHASTE_OBJS) -o $@ $(LINK_FLAGS)
# might lint some common things twice
.PHONY:
diff --git a/src/ast.c b/src/ast.c
new file mode 100644
index 0000000..3860c97
--- /dev/null
+++ b/src/ast.c
@@ -0,0 +1,225 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <math.h>
+
+#include <posthaste/ast.h>
+#include <posthaste/vec.h>
+
+static struct vec nodes = {0};
+
+static void destroy_ast_node(struct ast *n)
+{
+ if (!n)
+ return;
+
+ if (n->id)
+ free(n->id);
+
+ if (n->type)
+ free(n->type);
+
+ free(n);
+}
+
+void destroy_ast_nodes()
+{
+ foreach_vec(ni, nodes) {
+ struct ast *n = vect_at(struct ast *, nodes, ni);
+ destroy_ast_node(n);
+ }
+
+ vec_destroy(&nodes);
+}
+
+static struct ast *create_empty_ast()
+{
+ if (vec_uninit(nodes)) {
+ nodes = vec_create(sizeof(struct ast *));
+ }
+
+ struct ast *n = calloc(1, sizeof(struct ast));
+ vect_append(struct ast *, nodes, &n);
+ return n;
+}
+
+struct ast *gen_ast(enum ast_kind kind,
+ struct ast *a0,
+ struct ast *a1,
+ struct ast *a2,
+ char *id,
+ char *type,
+ int64_t v,
+ struct src_loc loc)
+{
+ struct ast *n = create_empty_ast();
+ n->k = kind;
+ n->a0 = a0;
+ n->a1 = a1;
+ n->a2 = a2;
+ n->id = id;
+ n->type = type;
+ n->v = v;
+ n->loc = loc;
+ return n;
+}
+
+static void dump(int depth, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ printf("//");
+ for (int i = 0; i < depth; ++i)
+ printf(" ");
+
+ vprintf(fmt, args);
+
+ va_end(args);
+}
+
+void type_dump(enum type_kind t)
+{
+ switch (t) {
+ case TYPE_DATE: printf("TYPE_DATE"); break;
+ case TYPE_INT: printf("TYPE_INT"); break;
+ default: printf("NO TYPE"); break;
+ }
+}
+
+void ast_dump(int depth, struct ast *n)
+{
+ if (!n) {
+ dump(depth, "{NULL}\n");
+ return;
+ }
+
+#define DUMP(x) case x: dump(depth, #x); break;
+ switch (n->k) {
+ DUMP(AST_BUILTIN_CALL);
+ DUMP(AST_PRINT_STRING);
+ DUMP(AST_PRINT_BOOL);
+ DUMP(AST_PRINT_DATE);
+ DUMP(AST_PRINT_INT);
+ DUMP(AST_CONST_INT);
+ DUMP(AST_CONST_DATE);
+ DUMP(AST_CONST_STRING);
+ DUMP(AST_DATE_ADD);
+ DUMP(AST_DATE_SUB);
+ DUMP(AST_DATE_DIFF);
+ DUMP(AST_EQ);
+ DUMP(AST_LT);
+ DUMP(AST_ADD);
+ DUMP(AST_SUB);
+ DUMP(AST_MUL);
+ DUMP(AST_DIV);
+ DUMP(AST_POS);
+ DUMP(AST_NEG);
+ DUMP(AST_UNLESS);
+ DUMP(AST_UNLESS_EXPR);
+ DUMP(AST_UNTIL);
+ DUMP(AST_FUNC_CALL);
+ DUMP(AST_PROC_CALL);
+ DUMP(AST_ID);
+ DUMP(AST_RETURN);
+ DUMP(AST_ASSIGN);
+ DUMP(AST_DOT);
+ DUMP(AST_ATTR);
+ DUMP(AST_PRINT);
+ DUMP(AST_FUNC_DEF);
+ DUMP(AST_PROC_DEF);
+ DUMP(AST_FORMAL_DEF);
+ DUMP(AST_VAR_DEF);
+ }
+#undef DUMP
+
+ depth++;
+
+ printf(" ("); type_dump(n->t); printf(")\n");
+
+ if (n->id)
+ dump(depth, "%s\n", n->id);
+
+ if (n->type)
+ dump(depth, "%s\n", n->type);
+
+ if (n->a0)
+ ast_dump_list(depth, n->a0);
+
+ if (n->a1)
+ ast_dump_list(depth, n->a1);
+
+ if (n->a2)
+ ast_dump_list(depth, n->a2);
+}
+
+void ast_dump_list(int depth, struct ast *root)
+{
+ if (!root) {
+ dump(depth, "{NULL}\n");
+ return;
+ }
+
+ foreach_node(n, root) {
+ ast_dump(depth, n);
+ }
+}
+
+int ast_visit(ast_callback_t before, ast_callback_t after, struct ast *n,
+ void *d)
+{
+ int ret = 0;
+ if (!n)
+ return ret;
+
+ if (before && (ret = before(n, d)))
+ return ret;
+
+ if (n->a0 && (ret = ast_visit_list(before, after, n->a0, d)))
+ return ret;
+
+ if (n->a1 && (ret = ast_visit_list(before, after, n->a1, d)))
+ return ret;
+
+ if (n->a2 && (ret = ast_visit_list(before, after, n->a2, d)))
+ return ret;
+
+ if (after && (ret = after(n, d)))
+ return ret;
+
+ return ret;
+}
+
+int ast_visit_list(ast_callback_t before, ast_callback_t after, struct ast *l,
+ void *d)
+{
+ int ret = 0;
+ foreach_node(n, l) {
+ if ((ret = ast_visit(before, after, n, d)))
+ return ret;
+ }
+
+ return ret;
+}
+
+struct ast *ast_last(struct ast *list)
+{
+ if (!list)
+ return NULL;
+
+ while (list->n)
+ list = list->n;
+
+ return list;
+}
+
+size_t ast_list_len(struct ast *l)
+{
+ size_t count = 0;
+ foreach_node(n, l) {
+ count++;
+ }
+
+ return count;
+}
diff --git a/src/check.c b/src/check.c
new file mode 100644
index 0000000..54929a1
--- /dev/null
+++ b/src/check.c
@@ -0,0 +1,767 @@
+#include <posthaste/check.h>
+
+#define UNUSED(x) (void)x
+
+struct state {
+ struct ast *parent;
+};
+
+int analyze_visibility(struct scope *scope, struct ast *n)
+{
+ switch (n->k) {
+ case AST_FUNC_DEF:
+ if (scope_add_func(scope, n))
+ return -1;
+
+ break;
+
+ case AST_PROC_DEF:
+ if (scope_add_proc(scope, n))
+ return -1;
+
+ break;
+ default:
+ }
+
+ return 0;
+}
+
+static bool has_type(struct ast *n)
+{
+ return n->t != 0;
+}
+
+static int analyze(struct state *state, struct scope *scope, struct ast *n);
+static int analyze_list(struct state *state, struct scope *scope, struct ast *l)
+{
+ foreach_node(n, l) {
+ if (analyze(state, scope, n))
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct ast *file_scope_find_analyzed(struct state *state, struct scope *scope, char *id)
+{
+ struct ast *exists = file_scope_find(scope, id);
+ if (!exists)
+ return NULL;
+
+ if (analyze(state, scope, exists))
+ return NULL;
+
+ return exists;
+}
+
+static int analyze_type(struct scope *scope, struct ast *n)
+{
+ if (!n->type) {
+ n->t = TYPE_VOID;
+ return 0;
+ }
+
+ if (same_id(n->type, "int")) {
+ n->t = TYPE_INT;
+ return 0;
+ }
+ else if (same_id(n->type, "date")) {
+ n->t = TYPE_DATE;
+ return 0;
+ }
+
+ semantic_error(scope, n, "no such type: %s", n->type);
+ return -1;
+}
+
+static int analyze_func_def(struct state *state, struct scope *scope, struct ast *f)
+{
+ UNUSED(state);
+ if (analyze_type(scope, f))
+ return -1;
+
+ /* slightly hacky, but allows recursion */
+ ast_set_flags(f, AST_FLAG_CHECKED);
+
+ struct scope *func_scope = create_scope();
+ scope_add_scope(scope, func_scope);
+ f->scope = func_scope;
+
+ struct state func_state = {.parent = f};
+ if (analyze_list(&func_state, func_scope, func_formals(f)))
+ return -1;
+
+ if (analyze_list(&func_state, func_scope, func_vars(f)))
+ return -1;
+
+ return analyze(&func_state, func_scope, func_body(f));
+}
+
+static int analyze_proc_def(struct state *state, struct scope *scope, struct ast *p)
+{
+ UNUSED(state);
+ if (analyze_type(scope, p))
+ return -1;
+
+ /* slightly hacky, but allows recursion */
+ ast_set_flags(p, AST_FLAG_CHECKED);
+
+ struct scope *proc_scope = create_scope();
+ scope_add_scope(scope, proc_scope);
+ p->scope = proc_scope;
+
+ struct state proc_state = {.parent = p};
+ if (analyze_list(&proc_state, proc_scope, proc_formals(p)))
+ return -1;
+
+ if (analyze_list(&proc_state, proc_scope, proc_vars(p)))
+ return -1;
+
+ if (analyze_list(&proc_state, proc_scope, proc_body(p)))
+ return -1;
+
+ struct ast *last = ast_last(proc_body(p));
+ if (p->t != TYPE_VOID && last->k != AST_RETURN) {
+ semantic_error(scope, p, "can't prove that proc returns");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int analyze_var_def(struct state *state, struct scope *scope, struct ast *n)
+{
+ if (scope_add_var(scope, n))
+ return -1;
+
+ struct ast *expr = var_expr(n);
+ if (analyze(state, scope, expr))
+ return -1;
+
+ n->t = expr->t;
+ return 0;
+}
+
+static int analyze_formal_def(struct state *state, struct scope *scope, struct ast *n)
+{
+ UNUSED(state);
+ if (analyze_type(scope, n))
+ return -1;
+
+ if (scope_add_formal(scope, n))
+ return -1;
+
+ return 0;
+}
+
+static int analyze_neg(struct state *state, struct scope *scope, struct ast *n)
+{
+ struct ast *expr = neg_expr(n);
+ if (analyze(state, scope, expr))
+ return -1;
+
+ if (expr->t != TYPE_INT) {
+ semantic_error(scope, n,
+ "negation requires %s, got %s\n",
+ type_str(TYPE_INT),
+ type_str(expr->t));
+ return -1;
+ }
+
+ n->t = expr->t;
+ return 0;
+}
+
+static int analyze_pos(struct state *state, struct scope *scope, struct ast *p)
+{
+ struct ast *expr = pos_expr(p);
+ if (analyze(state, scope, expr))
+ return -1;
+
+ if (expr->t != TYPE_INT) {
+ semantic_error(scope, p,
+ "unary pos requires %s, got %s\n",
+ type_str(TYPE_INT),
+ type_str(expr->t));
+ return -1;
+ }
+
+ p->t = expr->t;
+ return 0;
+}
+
+static int analyze_const_date(struct state *state, struct scope *scope, struct ast *d)
+{
+ UNUSED(state);
+ UNUSED(scope);
+ /* should be in correct format as the lexer took care of it */
+ d->t = TYPE_DATE;
+ return 0;
+}
+
+static int analyze_const_int(struct state *state, struct scope *scope, struct ast *i)
+{
+ UNUSED(state);
+ UNUSED(scope);
+ i->t = TYPE_INT;
+ return 0;
+}
+
+static int analyze_const_string(struct state *state, struct scope *scope, struct ast *s)
+{
+ UNUSED(state);
+ UNUSED(scope);
+ s->t = TYPE_STRING;
+ return 0;
+}
+
+static int analyze_eq(struct state *state, struct scope *scope, struct ast *e)
+{
+ struct ast *l = eq_l(e);
+ if (analyze(state, scope, l))
+ return -1;
+
+ struct ast *r = eq_r(e);
+ if (analyze(state, scope, r))
+ return -1;
+
+ if (r->t != l->t) {
+ semantic_error(scope, e, "type mismatch: %s vs %s\n",
+ type_str(l->t),
+ type_str(r->t));
+ return -1;
+ }
+
+ e->t = TYPE_BOOL;
+ return 0;
+}
+
+static int analyze_lt(struct state *state, struct scope *scope, struct ast *e)
+{
+ struct ast *l = lt_l(e);
+ if (analyze(state, scope, l))
+ return -1;
+
+ struct ast *r = lt_r(e);
+ if (analyze(state, scope, r))
+ return -1;
+
+ if (r->t != l->t) {
+ semantic_error(scope, e, "type mismatch: %s vs %s\n",
+ type_str(l->t),
+ type_str(r->t));
+ return -1;
+ }
+
+ e->t = TYPE_BOOL;
+ return 0;
+}
+
+static int analyze_add(struct state *state, struct scope *scope, struct ast *a)
+{
+ struct ast *l = add_l(a);
+ if (analyze(state, scope, l))
+ return -1;
+
+ struct ast *r = add_r(a);
+ if (analyze(state, scope, r))
+ return -1;
+
+ if (r->t == TYPE_DATE) {
+ semantic_error(scope, r, "date not allowed as rhs to addition");
+ return -1;
+ }
+
+ if (l->t == TYPE_DATE) {
+ a->k = AST_DATE_ADD;
+ }
+
+ a->t = l->t;
+ return 0;
+}
+
+static int analyze_sub(struct state *state, struct scope *scope, struct ast *s)
+{
+ struct ast *l = sub_l(s);
+ if (analyze(state, scope, l))
+ return -1;
+
+ struct ast *r = sub_r(s);
+ if (analyze(state, scope, r))
+ return -1;
+
+ if (l->t == TYPE_DATE && r->t == TYPE_DATE) {
+ s->k = AST_DATE_DIFF;
+ s->t = TYPE_INT;
+ return 0;
+ }
+
+ if (l->t == TYPE_DATE && r->t == TYPE_INT) {
+ s->k = AST_DATE_SUB;
+ s->t = TYPE_DATE;
+ return 0;
+ }
+
+ if (l->t == TYPE_INT && r->t == TYPE_INT) {
+ s->t = TYPE_INT;
+ return 0;
+ }
+
+ semantic_error(scope, s, "illegal subtraction types: %s, %s",
+ type_str(l->t), type_str(r->t));
+ return -1;
+}
+
+static int analyze_mul(struct state *state, struct scope *scope, struct ast *m)
+{
+ struct ast *l = mul_l(m);
+ if (analyze(state, scope, l))
+ return -1;
+
+ if (l->t != TYPE_INT) {
+ semantic_error(scope, l, "expected %s, got %s",
+ type_str(TYPE_INT), type_str(l->t));
+ return -1;
+ }
+
+ struct ast *r = mul_r(m);
+ if (analyze(state, scope, r))
+ return -1;
+
+ if (r->t != TYPE_INT) {
+ semantic_error(scope, r, "expected %s, got %s",
+ type_str(TYPE_INT), type_str(r->t));
+ return -1;
+ }
+
+ m->t = TYPE_INT;
+ return 0;
+}
+
+static int analyze_div(struct state *state, struct scope *scope, struct ast *d)
+{
+ struct ast *l = div_l(d);
+ if (analyze(state, scope, l))
+ return -1;
+
+ if (l->t != TYPE_INT) {
+ semantic_error(scope, l, "expected %s, got %s",
+ type_str(TYPE_INT), type_str(l->t));
+ return -1;
+ }
+
+ struct ast *r = div_r(d);
+ if (analyze(state, scope, r))
+ return -1;
+
+ if (r->t != TYPE_INT) {
+ semantic_error(scope, r, "expected %s, got %s",
+ type_str(TYPE_INT), type_str(r->t));
+ return -1;
+ }
+
+ d->t = TYPE_INT;
+ return 0;
+}
+
+static int analyze_print(struct state *state, struct scope *scope, struct ast *p)
+{
+ struct ast *items = print_items(p);
+ struct ast *fixups = NULL, *last_fixup = NULL;
+
+ foreach_node(n, items) {
+ if (analyze(state, scope, n))
+ return -1;
+
+ if (n->t == TYPE_VOID) {
+ semantic_error(scope, n, "void not printable");
+ return -1;
+ }
+
+ /* build new list of nodes, helps a bit when lowering */
+ struct ast *fixup = fixup_print_item(n);
+ if (!fixups)
+ fixups = fixup;
+
+ if (last_fixup) {
+ last_fixup->n = fixup;
+ }
+
+ last_fixup = fixup;
+ }
+
+ print_items(p) = fixups;
+ p->t = TYPE_VOID;
+ return 0;
+}
+
+static int analyze_return(struct state *state, struct scope *scope, struct ast *r)
+{
+ struct ast *parent = state->parent;
+ if (!parent) {
+ semantic_error(scope, r, "stray return");
+ return -1;
+ }
+
+
+ if (!has_type(parent)) {
+ semantic_error(scope, r, "stray return in proc without return type");
+ return -1;
+ }
+
+ struct ast *expr = return_expr(r);
+ if (analyze(state, scope, expr))
+ return -1;
+
+ if (expr->t != parent->t) {
+ semantic_error(scope, r, "return type mismatch: %s vs %s",
+ type_str(parent->t), type_str(expr->t));
+ return -1;
+ }
+
+ r->t = expr->t;
+ return 0;
+}
+
+static int analyze_id(struct state *state, struct scope *scope, struct ast *i)
+{
+ UNUSED(state);
+ struct ast *exists = file_scope_find_analyzed(state, scope, i->id);
+ if (!exists) {
+ semantic_error(scope, i, "no such symbol");
+ return -1;
+ }
+
+ if (exists->k != AST_FORMAL_DEF && exists->k != AST_VAR_DEF) {
+ semantic_error(scope, i, "no such variable");
+ return -1;
+ }
+
+ i->t = exists->t;
+ return 0;
+}
+
+static int analyze_dot(struct state *state, struct scope *scope, struct ast *d)
+{
+ struct ast *base = dot_base(d);
+ if (analyze(state, scope, base))
+ return -1;
+
+ if (base->t != TYPE_DATE) {
+ semantic_error(scope, d, "expected %d, got %d",
+ type_str(TYPE_DATE), type_str(base->t));
+ return -1;
+ }
+
+ d->t = TYPE_INT;
+ if (same_id(d->id, "day"))
+ return 0;
+
+ if (same_id(d->id, "month"))
+ return 0;
+
+ if (same_id(d->id, "year"))
+ return 0;
+
+ semantic_error(scope, d, "illegal write attribute %s", d->id);
+ return -1;
+}
+
+static int analyze_attr(struct state *state, struct scope *scope, struct ast *d)
+{
+ struct ast *base = attr_base(d);
+ if (analyze(state, scope, base))
+ return -1;
+
+ if (base->t != TYPE_DATE) {
+ semantic_error(scope, d, "expected %d, got %d",
+ type_str(TYPE_DATE), type_str(base->t));
+ return -1;
+ }
+
+ d->t = TYPE_INT;
+ if (same_id(d->id, "day"))
+ return 0;
+
+ if (same_id(d->id, "month"))
+ return 0;
+
+ if (same_id(d->id, "year"))
+ return 0;
+
+ if (same_id(d->id, "weekday")) {
+ d->t = TYPE_STRING;
+ return 0;
+ }
+
+ if (same_id(d->id, "weeknum"))
+ return 0;
+
+ semantic_error(scope, d, "illegal write attribute %s", d->id);
+ return -1;
+}
+
+static int analyze_assign(struct state *state, struct scope *scope, struct ast *n)
+{
+ struct ast *l = assign_l(n);
+ if (analyze(state, scope, l))
+ return -1;
+
+ struct ast *r = assign_r(n);
+ if (analyze(state, scope, r))
+ return -1;
+
+ if (l->t != r->t) {
+ semantic_error(scope, n, "type mismatch: %s vs %s",
+ type_str(l->t), type_str(r->t));
+ return -1;
+ }
+
+ n->t = l->t;
+ return 0;
+}
+
+static int analyze_today(struct state *state, struct scope *scope, struct ast *c)
+{
+ UNUSED(state);
+ if (ast_list_len(func_call_args(c)) != 0) {
+ semantic_error(scope, c, "expected 0 arguments, got %zd",
+ ast_list_len(func_call_args(c)));
+ return -1;
+ }
+
+ c->k = AST_BUILTIN_CALL;
+ c->t = TYPE_DATE;
+ return 0;
+}
+
+static int analyze_proc_call(struct state *state, struct scope *scope, struct ast *c)
+{
+ struct ast *exists = file_scope_find_analyzed(state, scope, proc_call_id(c));
+ if (!exists || exists->k != AST_PROC_DEF) {
+ semantic_error(scope, c, "no such proc");
+ return -1;
+ }
+
+ struct ast *args = proc_call_args(c);
+ if (analyze_list(state, scope, args))
+ return -1;
+
+ struct ast *formal = proc_formals(exists);
+ if (ast_list_len(formal) != ast_list_len(args)) {
+ semantic_error(scope, c, "expected %s args, got %s",
+ ast_list_len(formal), ast_list_len(args));
+ return -1;
+ }
+
+ foreach_node(a, args) {
+ if (a->t != formal->t) {
+ semantic_error(scope, c, "expected %s, got %s",
+ type_str(formal->t), type_str(a->t));
+ }
+
+ formal = formal->n;
+ }
+
+ c->t = exists->t;
+ return 0;
+}
+
+static int analyze_func_call(struct state *state, struct scope *scope, struct ast *c)
+{
+ /* handle special Today() built-in */
+ if (same_id(func_call_id(c), "Today"))
+ return analyze_today(state, scope, c);
+
+ struct ast *exists = file_scope_find_analyzed(state, scope, func_call_id(c));
+ if (!exists || exists->k != AST_FUNC_DEF) {
+ semantic_error(scope, c, "no such func");
+ return -1;
+ }
+
+ struct ast *args = func_call_args(c);
+ if (analyze_list(state, scope, args))
+ return -1;
+
+ struct ast *formal = func_formals(exists);
+ if (ast_list_len(formal) != ast_list_len(args)) {
+ semantic_error(scope, c, "expected %s args, got %s",
+ ast_list_len(formal), ast_list_len(args));
+ return -1;
+ }
+
+ foreach_node(a, args) {
+ if (a->t != formal->t) {
+ semantic_error(scope, c, "expected %s, got %s",
+ type_str(formal->t), type_str(a->t));
+ }
+
+ formal = formal->n;
+ }
+
+ c->t = exists->t;
+ return 0;
+}
+
+static int analyze_until(struct state *state, struct scope *scope, struct ast *n)
+{
+ struct scope *until_scope = create_scope();
+ scope_add_scope(scope, until_scope);
+
+ struct ast *body = until_body(n);
+ if (analyze_list(state, until_scope, body))
+ return -1;
+
+ struct ast *cond = until_cond(n);
+ if (analyze(state, until_scope, cond))
+ return -1;
+
+ if (cond->t != TYPE_BOOL) {
+ semantic_error(scope, cond, "expected %s, got %s",
+ type_str(TYPE_BOOL), type_str(cond->t));
+ return -1;
+ }
+
+ n->t = TYPE_VOID;
+ return 0;
+}
+
+static int analyze_unless(struct state *state, struct scope *scope, struct ast *n)
+{
+ struct scope *unless_scope = create_scope();
+ struct scope *otherwise_scope = create_scope();
+ scope_add_scope(scope, unless_scope);
+ scope_add_scope(scope, otherwise_scope);
+
+ struct ast *body = unless_body(n);
+ if (analyze_list(state, unless_scope, body))
+ return -1;
+
+ struct ast *cond = unless_cond(n);
+ if (analyze(state, scope, cond))
+ return -1;
+
+ if (cond->t != TYPE_BOOL) {
+ semantic_error(scope, cond, "expected %s, got %s",
+ type_str(TYPE_BOOL), type_str(cond->t));
+ return -1;
+ }
+
+ struct ast *otherwise = unless_otherwise(n);
+ if (otherwise && analyze_list(state, otherwise_scope, otherwise))
+ return -1;
+
+ n->t = TYPE_VOID;
+ return 0;
+}
+
+static int analyze_unless_expr(struct state *state, struct scope *scope, struct ast *n)
+{
+ struct ast *body = unless_expr_body(n);
+ if (analyze(state, scope, body))
+ return -1;
+
+ struct ast *cond = unless_expr_cond(n);
+ if (analyze(state, scope, cond))
+ return -1;
+
+ if (cond->t != TYPE_BOOL) {
+ semantic_error(scope, cond, "expected %s, got %s",
+ type_str(TYPE_BOOL), type_str(cond->t));
+ return -1;
+ }
+
+ struct ast *otherwise = unless_expr_otherwise(n);
+ if (analyze(state, scope, otherwise))
+ return -1;
+
+ if (body->t != otherwise->t) {
+ semantic_error(scope, n, "type mismatch: %s vs %s",
+ type_str(body->t), type_str(otherwise->t));
+ return -1;
+ }
+
+ n->t = body->t;
+ return 0;
+}
+
+static int analyze(struct state *state, struct scope *scope, struct ast *n)
+{
+ int ret = 0;
+
+ if (ast_flags(n, AST_FLAG_CHECKED)) {
+ assert(has_type(n));
+ return 0;
+ }
+
+ if (ast_flags(n, AST_FLAG_INIT)) {
+ semantic_error(scope, n, "semantic loop detected");
+ return -1;
+ }
+
+ ast_set_flags(n, AST_FLAG_INIT);
+
+ /* generally what we want to happen, special cases can handle themselves */
+ if (!n->scope)
+ n->scope = scope;
+
+ switch (n->k) {
+ case AST_FUNC_DEF: ret = analyze_func_def(state, scope, n); break;
+ case AST_PROC_DEF: ret = analyze_proc_def(state, scope, n); break;
+ case AST_VAR_DEF: ret = analyze_var_def(state, scope, n); break;
+ case AST_FORMAL_DEF: ret = analyze_formal_def(state, scope, n); break;
+ case AST_NEG: ret = analyze_neg(state, scope, n); break;
+ case AST_POS: ret = analyze_pos(state, scope, n); break;
+ case AST_CONST_DATE: ret = analyze_const_date(state, scope, n); break;
+ case AST_CONST_INT: ret = analyze_const_int(state, scope, n); break;
+ case AST_CONST_STRING: ret = analyze_const_string(state, scope, n); break;
+ case AST_EQ: ret = analyze_eq(state, scope, n); break;
+ case AST_LT: ret = analyze_lt(state, scope, n); break;
+ case AST_ADD: ret = analyze_add(state, scope, n); break;
+ case AST_SUB: ret = analyze_sub(state, scope, n); break;
+ case AST_MUL: ret = analyze_mul(state, scope, n); break;
+ case AST_DIV: ret = analyze_div(state, scope, n); break;
+ case AST_PRINT: ret = analyze_print(state, scope, n); break;
+ case AST_RETURN: ret = analyze_return(state, scope, n); break;
+ case AST_ID: ret = analyze_id(state, scope, n); break;
+ case AST_DOT: ret = analyze_dot(state, scope, n); break;
+ case AST_ATTR: ret = analyze_attr(state, scope, n); break;
+ case AST_ASSIGN: ret = analyze_assign(state, scope, n); break;
+ case AST_PROC_CALL: ret = analyze_proc_call(state, scope, n); break;
+ case AST_FUNC_CALL: ret = analyze_func_call(state, scope, n); break;
+ case AST_UNTIL: ret = analyze_until(state, scope, n); break;
+ case AST_UNLESS: ret = analyze_unless(state, scope, n); break;
+ case AST_UNLESS_EXPR: ret = analyze_unless_expr(state, scope, n); break;
+ default: break;
+ }
+
+ if (ret == 0) {
+ assert(has_type(n));
+ }
+
+ ast_set_flags(n, AST_FLAG_CHECKED);
+ return ret;
+}
+
+int check(struct scope *scope, struct ast *root)
+{
+ foreach_node(n, root) {
+ if (analyze_visibility(scope, n))
+ return -1;
+ }
+
+ foreach_node(n, root) {
+ struct state state = {0};
+ if (analyze(&state, scope, n))
+ return -1;
+ }
+
+#ifdef DEBUG
+ foreach_node(n, root) {
+ printf("// after checking:\n");
+ ast_dump(0, n);
+ }
+#endif
+
+ return 0;
+}
diff --git a/src/core.c b/src/core.c
index 7eca8bd..c50860e 100644
--- a/src/core.c
+++ b/src/core.c
@@ -3,9 +3,14 @@
#include <limits.h>
#include <errno.h>
-#include <posthaste/debug.h>
+#include <posthaste/execute.h>
#include <posthaste/parser.h>
+#include <posthaste/debug.h>
+#include <posthaste/scope.h>
+#include <posthaste/check.h>
+#include <posthaste/lower.h>
#include <posthaste/core.h>
+#include <posthaste/ast.h>
/**
* Read whole file into a buffer and return pointer to buffer.
@@ -41,28 +46,63 @@ static char *read_file(const char *fname, FILE *f)
int run(const char *fname)
{
+ int ret = 0;
+ const char *buf = NULL;
+ struct scope *scope = NULL;
+ struct parser *p = NULL;
+
FILE *f = fopen(fname, "rb");
if (!f) {
error("failed opening %s: %s\n", fname, strerror(errno));
return -1;
}
- const char *buf = read_file(fname, f);
+ buf = read_file(fname, f);
fclose(f);
- if (!buf)
- return -1;
+ if (!buf) {
+ ret = -1;
+ goto out;
+ }
- struct parser *p = create_parser();
- if (!p)
- return -1;
+ p = create_parser();
+ if (!p) {
+ ret = -1;
+ goto out;
+ }
parse(p, fname, buf);
- int ret = p->failed ? -1 : 0;
+ if (p->failed) {
+ ret = -1;
+ goto out;
+ }
- /* eventually do other stuff as well */
- free((void *)buf);
+ scope = create_scope();
+ if (!scope) {
+ ret = -1;
+ goto out;
+ }
+
+ scope_set_file(scope, fname, buf);
+
+ struct ast *ast = p->tree;
+ if (check(scope, ast)) {
+ ret = -1;
+ goto out;
+ }
+ if (lower_ast(ast)) {
+ ret = -1;
+ goto out;
+ }
+
+ execute();
+
+out:
+ free((void *)buf);
+ destroy_lowering();
+ destroy_scopes();
+ destroy_ast_nodes();
destroy_parser(p);
return ret;
}
diff --git a/src/date.c b/src/date.c
index 7e6bc77..11f35c4 100644
--- a/src/date.c
+++ b/src/date.c
@@ -23,6 +23,36 @@ ph_date_t date_from_numbers(unsigned year, unsigned month, unsigned day)
return year << 9 | month << 5 | day;
}
+struct tm tm_from_date(ph_date_t date)
+{
+ unsigned year = 0;
+ unsigned month = 0;
+ unsigned day = 0;
+ date_split(date, &year, &month, &day);
+
+ struct tm time = {.tm_year = year - 1900, .tm_mon = month - 1, .tm_mday = day};
+ mktime(&time);
+ return time;
+}
+
+ph_date_t date_from_tm(struct tm time)
+{
+ unsigned year = time.tm_year + 1900;
+ unsigned month = time.tm_mon + 1;
+ unsigned day = time.tm_mday;
+ return date_from_numbers(year, month, day);
+}
+
+ph_date_t current_date()
+{
+ time_t t = time(NULL);
+ struct tm *time = localtime(&t);
+ unsigned year = time->tm_year + 1900;
+ unsigned month = time->tm_mon + 1;
+ unsigned day = time->tm_mday;
+ return date_from_numbers(year, month, day);
+}
+
void date_split(ph_date_t date, unsigned *year, unsigned *month, unsigned *day)
{
if (year) *year = date >> 9;
diff --git a/src/execute.c b/src/execute.c
new file mode 100644
index 0000000..0d321d7
--- /dev/null
+++ b/src/execute.c
@@ -0,0 +1,367 @@
+#include <math.h>
+
+#include <posthaste/execute.h>
+#include <posthaste/lower.h>
+#include <posthaste/date.h>
+#include <posthaste/vec.h>
+
+#define UNUSED(x) (void)x
+
+#define DEF(x) \
+static void exec_##x(struct insn i, size_t sp, struct vec *stack, struct vec *globals)
+
+static int64_t load(struct loc l, size_t sp, struct vec *stack, struct vec *globals)
+{
+ if (l.l)
+ return vect_at(int64_t, *stack, l.o + sp);
+
+ return vect_at(int64_t, *globals, l.o);
+}
+
+static void store(struct loc l, int64_t v, size_t sp, struct vec *stack, struct vec *globals)
+{
+ if (l.l) {
+ vect_at(int64_t, *stack, l.o + sp) = v;
+ return;
+ }
+
+ vect_at(int64_t, *globals, l.o) = v;
+}
+
+#define get(l) \
+ load(l, sp, stack, globals)
+
+#define put(l, v) \
+ store(l, v, sp, stack, globals)
+
+DEF(MOVE) {
+ int64_t i0 = get(i.i0);
+ put(i.o, i0);
+}
+
+DEF(ADD) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t o = i0 + i1;
+ put(i.o, o);
+}
+
+DEF(SUB) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t o = i0 - i1;
+ put(i.o, o);
+}
+
+DEF(MUL) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t o = i0 * i1;
+ put(i.o, o);
+}
+
+DEF(DIV) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t o = i0 / i1;
+ put(i.o, o);
+}
+
+DEF(PRINT_DATE) {
+ int64_t i0 = get(i.i0);
+ char str[11] = {0};
+ date_to_string(str, i0);
+ printf("%s", str);
+}
+
+DEF(PRINT_INT) {
+ int64_t i0 = get(i.i0);
+ printf("%lli", (long long)i0);
+}
+
+DEF(PRINT_STRING) {
+ int64_t i0 = get(i.i0);
+ printf("%s", (const char *)i0);
+}
+
+DEF(PRINT_BOOL) {
+ int64_t i0 = get(i.i0);
+ printf("%s", i0 ? "true" : "false");
+}
+
+DEF(PRINT_NEWLINE) {
+ UNUSED(i);
+ UNUSED(sp);
+ UNUSED(stack);
+ UNUSED(globals);
+ printf("\n");
+}
+
+DEF(PRINT_SPACE) {
+ UNUSED(i);
+ UNUSED(sp);
+ UNUSED(stack);
+ UNUSED(globals);
+ printf(" ");
+}
+
+DEF(CONST) {
+ put(i.o, i.v);
+}
+
+DEF(EQ) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t b = i0 == i1;
+ put(i.o, b);
+}
+
+DEF(LT) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t b = i0 < i1;
+ put(i.o, b);
+}
+
+DEF(NEG) {
+ int64_t i0 = get(i.i0);
+ put(i.o, -i0);
+}
+
+DEF(LOAD_DAY) {
+ int64_t i0 = get(i.i0);
+ unsigned day = 0;
+ date_split((ph_date_t)i0, NULL, NULL, &day);
+ put(i.o, (int64_t)day);
+}
+
+DEF(LOAD_MONTH) {
+ int64_t i0 = get(i.i0);
+ unsigned month = 0;
+ date_split((ph_date_t)i0, NULL, &month, NULL);
+ put(i.o, (int64_t)month);
+}
+
+DEF(LOAD_YEAR) {
+ int64_t i0 = get(i.i0);
+ unsigned year = 0;
+ date_split((ph_date_t)i0, &year, NULL, NULL);
+ put(i.o, (int64_t)year);
+}
+
+DEF(LOAD_WEEKDAY) {
+ int64_t i0 = get(i.i0);
+ struct tm time = tm_from_date((ph_date_t)i0);
+
+ const char *day = "Sunday";
+ switch (time.tm_wday) {
+ case 0: day = "Sunday"; break;
+ case 1: day = "Monday"; break;
+ case 2: day = "Tuesday"; break;
+ case 3: day = "Wednesday"; break;
+ case 4: day = "Thursday"; break;
+ case 5: day = "Friday"; break;
+ case 6: day = "Saturday"; break;
+ }
+
+ put(i.o, (int64_t)day);
+}
+
+DEF(LOAD_WEEKNUM) {
+ int64_t i0 = get(i.i0);
+ struct tm time = tm_from_date((ph_date_t)i0);
+ put(i.o, time.tm_yday / 7);
+}
+
+DEF(STORE_DAY) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ unsigned year = 0;
+ unsigned month = 0;
+ date_split((ph_date_t)i0, &year, &month, NULL);
+ ph_date_t date = date_from_numbers(year, month, i1);
+ put(i.o, (int64_t)date);
+}
+
+DEF(STORE_MONTH) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ unsigned year = 0;
+ unsigned day = 0;
+ date_split((ph_date_t)i0, &year, NULL, &day);
+ ph_date_t date = date_from_numbers(year, i1, day);
+ put(i.o, (int64_t)date);
+}
+
+DEF(STORE_YEAR) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ unsigned month = 0;
+ unsigned day = 0;
+ date_split((ph_date_t)i0, NULL, &month, &day);
+ ph_date_t date = date_from_numbers(i1, month, day);
+ put(i.o, (int64_t)date);
+}
+
+DEF(TODAY) {
+ ph_date_t date = current_date();
+ put(i.o, (int64_t)date);
+}
+
+DEF(DATE_ADD) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ struct tm time = tm_from_date((ph_date_t)i0);
+ time.tm_mday += i1;
+ mktime(&time);
+
+ ph_date_t date = date_from_tm(time);
+ put(i.o, (int64_t)date);
+}
+
+DEF(DATE_SUB) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ struct tm time = tm_from_date((ph_date_t)i0);
+ time.tm_mday -= i1;
+ mktime(&time);
+
+ ph_date_t date = date_from_tm(time);
+ put(i.o, (int64_t)date);
+}
+
+DEF(DATE_DIFF) {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ struct tm time0 = tm_from_date((ph_date_t)i0);
+ struct tm time1 = tm_from_date((ph_date_t)i1);
+
+ /* close enough at least */
+ time_t t0 = mktime(&time0);
+ time_t t1 = mktime(&time1);
+ double seconds = difftime(t0, t1);
+ int64_t days = round(seconds / 86400);
+ put(i.o, days);
+}
+
+static int64_t exec_func(struct fn *f, struct vec *args, size_t sp, struct vec *stack, struct vec *globals)
+{
+ /* move args to formal locations */
+ size_t i = 0;
+ foreach_vec(ai, *args) {
+ int64_t a = vect_at(int64_t, *args, ai);
+ vect_at(int64_t, *stack, sp + i) = a;
+ i += 1;
+ }
+
+ vec_reset(args);
+
+ size_t pc = 0;
+ int64_t retval = 0;
+ struct vec insns = f->insns;
+ while (1) {
+ struct insn i = vect_at(struct insn, insns, pc);
+
+#define DO(x) case x: exec_##x(i, sp, stack, globals); break;
+ switch (i.k) {
+ /* special cases first */
+ case LABEL: break;
+ case STOP: return 0;
+ case RET: return get(i.i0);
+ case RETVAL: put(i.o, retval); break;
+ case J: pc = i.v; continue;
+ case B: {
+ int64_t i0 = get(i.i0);
+ if (i0) {
+ pc = i.v;
+ continue;
+ }
+
+ break;
+ }
+
+ case BZ: {
+ int64_t i0 = get(i.i0);
+ if (!i0) {
+ pc = i.v;
+ continue;
+ }
+
+ break;
+ }
+
+ case ARG: {
+ int64_t a = get(i.i0);
+ vect_append(int64_t, *args, &a);
+ break;
+ }
+
+ case CALL: {
+ struct fn *cf = find_fn(i.v);
+ assert(cf);
+
+ retval = exec_func(cf, args, sp + f->max_sp, stack, globals);
+ break;
+ }
+
+ DO(MOVE);
+ DO(ADD);
+ DO(SUB);
+ DO(MUL);
+ DO(DIV);
+ DO(CONST);
+ DO(PRINT_DATE);
+ DO(PRINT_INT);
+ DO(PRINT_BOOL);
+ DO(PRINT_STRING);
+ DO(PRINT_NEWLINE);
+ DO(PRINT_SPACE);
+ DO(LOAD_DAY);
+ DO(LOAD_MONTH);
+ DO(LOAD_YEAR);
+ DO(LOAD_WEEKDAY);
+ DO(LOAD_WEEKNUM);
+ DO(STORE_DAY);
+ DO(STORE_MONTH);
+ DO(STORE_YEAR);
+ DO(DATE_ADD);
+ DO(DATE_SUB);
+ DO(DATE_DIFF);
+ DO(TODAY);
+ DO(EQ);
+ DO(LT);
+ DO(NEG);
+ }
+#undef DO
+
+ pc += 1;
+ }
+}
+
+void execute()
+{
+ struct fn *main = find_fn(0);
+ assert(main);
+
+ struct vec stack = vec_create(sizeof(int64_t));
+ /* arbitrary amount */
+ vec_reserve(&stack, 65535);
+
+ struct vec globals = vec_create(sizeof(int64_t));
+ /* should really take the value calculated during lowering */
+ vec_reserve(&globals, 65535);
+
+ struct vec args = vec_create(sizeof(int64_t));
+
+ exec_func(main, &args, 0, &stack, &globals);
+
+ vec_destroy(&args);
+ vec_destroy(&stack);
+ vec_destroy(&globals);
+}
diff --git a/src/lexer.l b/src/lexer.l
index 6f58bd5..4de6ead 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -154,7 +154,9 @@ STRING \"(\\.|[^"\\])*\"
"end" {return END;}
{STRING} {
- yylval->str = yytext;
+ /* skip quotation marks */
+ yylval->str = strdup(yytext + 1);
+ yylval->str[strlen(yylval->str) - 1] = '\0';
return STRING;
}
@@ -169,17 +171,17 @@ STRING \"(\\.|[^"\\])*\"
}
{IDENT} {
- yylval->str = yytext;
+ yylval->str = strdup(yytext);
return IDENT;
}
{FUNC_IDENT} {
- yylval->str = yytext;
+ yylval->str = strdup(yytext);
return FUNC_IDENT;
}
{PROC_IDENT} {
- yylval->str = yytext;
+ yylval->str = strdup(yytext);
return PROC_IDENT;
}
diff --git a/src/lower.c b/src/lower.c
new file mode 100644
index 0000000..f4ca28e
--- /dev/null
+++ b/src/lower.c
@@ -0,0 +1,730 @@
+#include <stdlib.h>
+
+#include <posthaste/lower.h>
+#include <posthaste/scope.h>
+
+#define UNUSED(x) (void)x
+
+static struct vec fns = {0};
+/* zero is unintialized, global 1 reserved as null, so skip two first globals */
+static size_t globals = 2;
+
+static void lower(struct fn *f, struct ast *n);
+static void lower_list(struct fn *f, struct ast *l)
+{
+ foreach_node(n, l) {
+ lower(f, n);
+ }
+}
+
+static size_t loc_as_int(struct loc l)
+{
+ size_t a = l.o;
+ a |= (size_t)(l.l) << (sizeof(size_t) * 8 - 1);
+ return a;
+}
+
+static struct loc build_local_loc(size_t idx)
+{
+ return (struct loc){.l = 1, .o = idx};
+}
+
+static struct loc build_global_loc(size_t idx)
+{
+ return (struct loc){.l = 0, .o = idx};
+}
+
+static struct loc null_loc()
+{
+ /* don't exactly love this but I guess it works */
+ return build_global_loc(1);
+}
+
+static void output_insn(struct fn *f, enum insn_kind k, struct loc o, struct loc i0, struct loc i1, int64_t v)
+{
+ struct insn i = {.k = k, .o = o, .i0 = i0, .i1 = i1, .v = v};
+ vect_append(struct insn, f->insns, &i);
+}
+
+static void lower_var(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp++);
+ struct ast *expr = var_expr(n);
+ lower(f, expr);
+ output_insn(f, MOVE, n->l, expr->l, null_loc(), 0);
+}
+
+static void lower_formal(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp++);
+}
+
+static void lower_date(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+ output_insn(f, CONST, n->l, null_loc(), null_loc(), n->v);
+}
+
+static void lower_string(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+ output_insn(f, CONST, n->l, null_loc(), null_loc(), (int64_t)n->id);
+}
+
+static void lower_int(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+ output_insn(f, CONST, n->l, null_loc(), null_loc(), n->v);
+}
+
+static void lower_add(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+ struct ast *l = add_l(n);
+ struct ast *r = add_r(n);
+
+ f->sp += 1; lower(f, l);
+ f->sp += 1; lower(f, r);
+ f->sp -= 2;
+
+ output_insn(f, ADD, n->l, l->l, r->l, 0);
+}
+
+static void lower_sub(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+ struct ast *l = sub_l(n);
+ struct ast *r = sub_r(n);
+
+ f->sp += 1; lower(f, l);
+ f->sp += 1; lower(f, r);
+ f->sp -= 2;
+
+ output_insn(f, SUB, n->l, l->l, r->l, 0);
+}
+
+static void lower_mul(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+ struct ast *l = mul_l(n);
+ struct ast *r = mul_r(n);
+
+ f->sp += 1; lower(f, l);
+ f->sp += 1; lower(f, r);
+ f->sp -= 2;
+
+ output_insn(f, MUL, n->l, l->l, r->l, 0);
+}
+
+static void lower_div(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+ struct ast *l = div_l(n);
+ struct ast *r = div_r(n);
+
+ f->sp += 1; lower(f, l);
+ f->sp += 1; lower(f, r);
+ f->sp -= 2;
+
+ output_insn(f, DIV, n->l, l->l, r->l, 0);
+}
+
+static void lower_dot_assign(struct fn *f, struct ast *n)
+{
+ struct ast *r = assign_r(n);
+ struct ast *l = assign_l(n);
+
+ lower(f, r);
+
+ struct ast *base = dot_base(l);
+ lower(f, base);
+
+ enum insn_kind store = STORE_DAY;
+ if (same_id(l->id, "day"))
+ store = STORE_DAY;
+
+ else if (same_id(l->id, "month"))
+ store = STORE_MONTH;
+
+ else if (same_id(l->id, "year"))
+ store = STORE_YEAR;
+ else
+ abort();
+
+ output_insn(f, store, base->l, base->l, r->l, 0);
+ n->l = base->l;
+}
+
+static void lower_assign(struct fn *f, struct ast *n)
+{
+ struct ast *l = assign_l(n);
+ if (l->k == AST_DOT)
+ return lower_dot_assign(f, n);
+
+ struct ast *r = assign_r(n);
+
+ lower(f, r);
+ lower(f, l);
+ output_insn(f, MOVE, l->l, r->l, null_loc(), 0);
+ n->l = null_loc();
+}
+
+static void lower_id(struct fn *f, struct ast *n)
+{
+ UNUSED(f);
+ struct ast *exists = file_scope_find(n->scope, n->id);
+ assert(exists);
+
+ n->l = exists->l;
+}
+
+static void lower_return(struct fn *f, struct ast *n)
+{
+ struct ast *expr = return_expr(n);
+ lower(f, expr);
+ output_insn(f, RET, null_loc(), expr->l, null_loc(), 0);
+ n->l = null_loc();
+}
+
+static void lower_attr(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+
+ struct ast *base = attr_base(n);
+ lower(f, base);
+
+ enum insn_kind load = LOAD_DAY;
+ if (same_id(n->id, "day"))
+ load = LOAD_DAY;
+
+ else if (same_id(n->id, "month"))
+ load = LOAD_MONTH;
+
+ else if (same_id(n->id, "year"))
+ load = LOAD_YEAR;
+
+ else if (same_id(n->id, "weekday"))
+ load = LOAD_WEEKDAY;
+
+ else if (same_id(n->id, "weeknum"))
+ load = LOAD_WEEKNUM;
+
+ else
+ abort();
+
+ output_insn(f, load, n->l, base->l, null_loc(), 0);
+}
+
+static void lower_print(struct fn *f, struct ast *p)
+{
+ foreach_node(n, print_items(p)) {
+ lower(f, n);
+
+ /* don't print space on last element */
+ if (n->n)
+ output_insn(f, PRINT_SPACE, null_loc(), null_loc(), null_loc(), 0);
+ }
+
+ output_insn(f, PRINT_NEWLINE, null_loc(), null_loc(), null_loc(), 0);
+ p->l = null_loc();
+}
+
+static void lower_proc_call(struct fn *f, struct ast *c)
+{
+ size_t count = 0;
+ foreach_node(n, proc_call_args(c)) {
+ f->sp += 1; lower(f, n); count += 1;
+ }
+
+ size_t i = 0;
+ foreach_node(n, proc_call_args(c)) {
+ output_insn(f, ARG, null_loc(), n->l, null_loc(), i);
+ i++;
+ }
+
+ struct ast *def = file_scope_find(c->scope, proc_call_id(c));
+ output_insn(f, CALL, null_loc(), null_loc(), null_loc(), loc_as_int(def->l));
+
+ c->l = build_local_loc(f->sp);
+ if (c->t != TYPE_VOID)
+ output_insn(f, RETVAL, c->l, null_loc(), null_loc(), 0);
+
+ f->sp -= count;
+}
+
+static void lower_func_call(struct fn *f, struct ast *c)
+{
+ size_t count = 0;
+ foreach_node(n, func_call_args(c)) {
+ f->sp += 1; lower(f, n); count += 1;
+ }
+
+ size_t i = 0;
+ foreach_node(n, func_call_args(c)) {
+ output_insn(f, ARG, null_loc(), n->l, null_loc(), i);
+ i++;
+ }
+
+ struct ast *def = file_scope_find(c->scope, func_call_id(c));
+ output_insn(f, CALL, null_loc(), null_loc(), null_loc(), loc_as_int(def->l));
+ c->l = build_local_loc(f->sp);
+ if (c->t != TYPE_VOID)
+ output_insn(f, RETVAL, c->l, null_loc(), null_loc(), 0);
+
+ f->sp -= count;
+}
+
+static void lower_eq(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+ struct ast *l = eq_l(n);
+ struct ast *r = eq_r(n);
+
+ f->sp += 1; lower(f, l);
+ f->sp += 1; lower(f, r);
+ f->sp -= 2;
+
+ output_insn(f, EQ, n->l, l->l, r->l, 0);
+}
+
+static void lower_lt(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+ struct ast *l = lt_l(n);
+ struct ast *r = lt_r(n);
+
+ f->sp += 1; lower(f, l);
+ f->sp += 1; lower(f, r);
+ f->sp -= 2;
+
+ output_insn(f, LT, n->l, l->l, r->l, 0);
+}
+
+static void lower_pos(struct fn *f, struct ast *n)
+{
+ struct ast *expr = pos_expr(n);
+ lower(f, expr);
+ n->l = expr->l;
+}
+
+static void lower_neg(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+ struct ast *expr = neg_expr(n);
+ f->sp += 1; lower(f, expr);
+ f->sp -= 1;
+
+ output_insn(f, NEG, n->l, expr->l, null_loc(), 0);
+}
+
+static void lower_print_date(struct fn *f, struct ast *n)
+{
+ struct ast *expr = print_date_expr(n);
+ lower(f, expr);
+ output_insn(f, PRINT_DATE, null_loc(), expr->l, null_loc(), 0);
+ n->l = null_loc();
+}
+
+static void lower_print_int(struct fn *f, struct ast *n)
+{
+ struct ast *expr = print_int_expr(n);
+ lower(f, expr);
+ output_insn(f, PRINT_INT, null_loc(), expr->l, null_loc(), 0);
+ n->l = null_loc();
+}
+
+static void lower_print_string(struct fn *f, struct ast *n)
+{
+ struct ast *expr = print_string_expr(n);
+ lower(f, expr);
+ output_insn(f, PRINT_STRING, null_loc(), expr->l, null_loc(), 0);
+ n->l = null_loc();
+}
+
+static void lower_print_bool(struct fn *f, struct ast *n)
+{
+ struct ast *expr = print_bool_expr(n);
+ lower(f, expr);
+ output_insn(f, PRINT_BOOL, null_loc(), expr->l, null_loc(), 0);
+ n->l = null_loc();
+}
+
+static void lower_date_add(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+
+ struct ast *l = date_add_l(n);
+ struct ast *r = date_add_r(n);
+
+ f->sp += 1; lower(f, l);
+ f->sp += 1; lower(f, r);
+ f->sp -= 2;
+
+ output_insn(f, DATE_ADD, n->l, l->l, r->l, 0);
+}
+
+static void lower_date_sub(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+
+ struct ast *l = date_sub_l(n);
+ struct ast *r = date_sub_r(n);
+
+ f->sp += 1; lower(f, l);
+ f->sp += 1; lower(f, r);
+ f->sp -= 2;
+
+ output_insn(f, DATE_SUB, n->l, l->l, r->l, 0);
+}
+
+static void lower_date_diff(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+
+ struct ast *l = date_diff_l(n);
+ struct ast *r = date_diff_r(n);
+
+ f->sp += 1; lower(f, l);
+ f->sp += 1; lower(f, r);
+ f->sp -= 2;
+
+ output_insn(f, DATE_DIFF, n->l, l->l, r->l, 0);
+}
+
+static void lower_until(struct fn *f, struct ast *n)
+{
+ size_t off = vec_len(&f->insns);
+ output_insn(f, LABEL, null_loc(), null_loc(), null_loc(), 0);
+
+ lower_list(f, until_body(n));
+
+ struct ast *cond = until_cond(n);
+ lower(f, cond);
+
+ output_insn(f, BZ, null_loc(), cond->l, null_loc(), off);
+ n->l = null_loc();
+}
+
+static void patch_branch(struct fn *f, size_t branch, size_t off)
+{
+ struct insn i = vect_at(struct insn, f->insns, branch);
+ i.v = off;
+ vect_at(struct insn, f->insns, branch) = i;
+}
+
+static void lower_unless(struct fn *f, struct ast *n)
+{
+ struct ast *cond = unless_cond(n);
+ lower(f, cond);
+
+ size_t branch = vec_len(&f->insns);
+ /* placeholder */
+ output_insn(f, B, null_loc(), cond->l, null_loc(), 0);
+
+ struct ast *body = unless_body(n);
+ lower_list(f, body);
+
+ size_t jump = vec_len(&f->insns);
+ /* placeholder */
+ output_insn(f, J, null_loc(), null_loc(), null_loc(), 0);
+
+ size_t off = vec_len(&f->insns);
+ patch_branch(f, branch, off);
+
+ struct ast *otherwise = unless_otherwise(n);
+ lower_list(f, otherwise);
+
+ off = vec_len(&f->insns);
+ patch_branch(f, jump, off);
+
+ n->l = null_loc();
+}
+
+static void lower_unless_expr(struct fn *f, struct ast *n)
+{
+ n->l = build_local_loc(f->sp);
+
+ struct ast *cond = unless_expr_cond(n);
+ lower(f, cond);
+
+ size_t branch = vec_len(&f->insns);
+ /* placeholder */
+ output_insn(f, B, null_loc(), cond->l, null_loc(), 0);
+
+ struct ast *body = unless_expr_body(n);
+ lower(f, body);
+
+ output_insn(f, MOVE, n->l, body->l, null_loc(), 0);
+
+ size_t jump = vec_len(&f->insns);
+ /* placeholder */
+ output_insn(f, J, null_loc(), null_loc(), null_loc(), 0);
+
+ size_t off = vec_len(&f->insns);
+ patch_branch(f, branch, off);
+
+ struct ast *otherwise = unless_expr_otherwise(n);
+ lower(f, otherwise);
+ output_insn(f, MOVE, n->l, otherwise->l, null_loc(), 0);
+
+ off = vec_len(&f->insns);
+ patch_branch(f, jump, off);
+}
+
+static void lower_builtin_call(struct fn *f, struct ast *n)
+{
+ /* for now we only support Today(), which doesn't have any args */
+ assert(same_id(builtin_call_id(n), "Today"));
+
+ n->l = build_local_loc(f->sp);
+ output_insn(f, TODAY, n->l, null_loc(), null_loc(), 0);
+}
+
+static void lower(struct fn *f, struct ast *n)
+{
+ if (f->max_sp < f->sp)
+ f->max_sp = f->sp;
+
+ switch (n->k) {
+ case AST_PROC_DEF: break;
+ case AST_FUNC_DEF: break;
+ case AST_VAR_DEF: lower_var(f, n); break;
+ case AST_FORMAL_DEF: lower_formal(f, n); break;
+ case AST_CONST_STRING: lower_string(f, n); break;
+ case AST_CONST_DATE: lower_date(f, n); break;
+ case AST_CONST_INT: lower_int(f, n); break;
+ case AST_ASSIGN: lower_assign(f, n); break;
+ case AST_ADD: lower_add(f, n); break;
+ case AST_SUB: lower_sub(f, n); break;
+ case AST_MUL: lower_mul(f, n); break;
+ case AST_DIV: lower_div(f, n); break;
+ case AST_ID: lower_id(f, n); break;
+ case AST_RETURN: lower_return(f, n); break;
+ case AST_ATTR: lower_attr(f, n); break;
+ case AST_PRINT: lower_print(f, n); break;
+ case AST_PROC_CALL: lower_proc_call(f, n); break;
+ case AST_FUNC_CALL: lower_func_call(f, n); break;
+ case AST_BUILTIN_CALL: lower_builtin_call(f, n); break;
+ case AST_EQ: lower_eq(f, n); break;
+ case AST_LT: lower_lt(f, n); break;
+ case AST_POS: lower_pos(f, n); break;
+ case AST_NEG: lower_neg(f, n); break;
+ case AST_PRINT_DATE: lower_print_date(f, n); break;
+ case AST_PRINT_STRING: lower_print_string(f, n); break;
+ case AST_PRINT_BOOL: lower_print_bool(f, n); break;
+ case AST_PRINT_INT: lower_print_int(f, n); break;
+ case AST_DATE_ADD: lower_date_add(f, n); break;
+ case AST_DATE_SUB: lower_date_sub(f, n); break;
+ case AST_DATE_DIFF: lower_date_diff(f, n); break;
+ case AST_UNTIL: lower_until(f, n); break;
+ case AST_UNLESS: lower_unless(f, n); break;
+ case AST_UNLESS_EXPR: lower_unless_expr(f, n); break;
+
+ /* handled by assign */
+ case AST_DOT: break;
+ }
+
+ assert(loc_as_int(n->l) > 0);
+}
+
+static void lower_global_var(struct fn *f, struct ast *n) {
+ n->l = build_global_loc(globals++);
+ struct ast *expr = var_expr(n);
+ lower(f, expr);
+ output_insn(f, MOVE, n->l, expr->l, null_loc(), 0);
+}
+
+static void add_proc(struct ast *n) {
+ size_t idx = vec_len(&fns);
+ n->l = build_global_loc(idx);
+ struct fn f = {.name = proc_id(n),
+ .idx = idx,
+ .sp = 0,
+ .insns = vec_create(sizeof(struct insn))};
+
+ vect_append(struct fn, fns, &f);
+}
+
+static void add_func(struct ast *n) {
+ size_t idx = vec_len(&fns);
+ n->l = build_global_loc(idx);
+ struct fn f = {.name = func_id(n),
+ .idx = idx,
+ .sp = 0,
+ .insns = vec_create(sizeof(struct insn))};
+
+ vect_append(struct fn, fns, &f);
+}
+
+static void lower_proc_def(struct ast *d)
+{
+ struct fn *f = vec_at(&fns, d->l.o);
+ assert(f);
+
+ lower_list(f, proc_formals(d));
+ lower_list(f, proc_vars(d));
+ lower_list(f, proc_body(d));
+
+ if (d->t == TYPE_VOID)
+ output_insn(f, STOP, null_loc(), null_loc(), null_loc(), 0);
+}
+
+static void lower_func_def(struct ast *d)
+{
+ struct fn *f = vec_at(&fns, d->l.o);
+ assert(f);
+
+ lower_list(f, func_formals(d));
+ lower_list(f, func_vars(d));
+ lower_list(f, func_body(d));
+}
+
+#ifdef DEBUG
+static void dump_loc(struct loc l)
+{
+ if (is_null_loc(l)) {
+ printf("null, ");
+ return;
+ }
+
+ bool local = l.l;
+ if (local) {
+ printf("l");
+ } else {
+ printf("g");
+ }
+
+ size_t val = l.o;
+ printf("%zd, ", val);
+}
+
+static void dump_val(int64_t v)
+{
+ printf("%lli", (long long)v);
+}
+
+static void dump_insn(struct insn i, size_t addr)
+{
+ printf("//%8zd: ", addr);
+#define DUMP(x) case x: printf(#x); break;
+ switch (i.k) {
+ DUMP(TODAY);
+ DUMP(CALL);
+ DUMP(MOVE);
+ DUMP(ADD);
+ DUMP(SUB);
+ DUMP(MUL);
+ DUMP(DIV);
+ DUMP(ARG);
+ DUMP(STOP);
+ DUMP(RETVAL);
+ DUMP(PRINT_DATE);
+ DUMP(PRINT_INT);
+ DUMP(PRINT_STRING);
+ DUMP(PRINT_NEWLINE);
+ DUMP(PRINT_SPACE);
+ DUMP(PRINT_BOOL);
+ DUMP(DATE_ADD);
+ DUMP(DATE_SUB);
+ DUMP(DATE_DIFF);
+ DUMP(STORE_DAY);
+ DUMP(STORE_MONTH);
+ DUMP(STORE_YEAR);
+ DUMP(LOAD_DAY);
+ DUMP(LOAD_MONTH);
+ DUMP(LOAD_YEAR);
+ DUMP(LOAD_WEEKDAY);
+ DUMP(LOAD_WEEKNUM);
+ DUMP(RET);
+ DUMP(CONST);
+ DUMP(EQ);
+ DUMP(LT);
+ DUMP(NEG);
+ DUMP(B);
+ DUMP(BZ);
+ DUMP(J);
+ DUMP(LABEL);
+ }
+#undef DUMP
+
+ printf(" ");
+
+ dump_loc(i.o);
+ dump_loc(i.i0);
+ dump_loc(i.i1);
+ dump_val(i.v);
+
+ printf("\n");
+}
+
+static void dump_insns(struct fn *f)
+{
+ size_t addr = 0;
+ foreach_vec(ii, f->insns) {
+ struct insn i = vect_at(struct insn, f->insns, ii);
+ dump_insn(i, addr);
+ addr++;
+ }
+}
+#endif /* DEBUG */
+
+struct fn *find_fn(size_t idx)
+{
+ return vec_at(&fns, idx);
+}
+
+int lower_ast(struct ast *tree)
+{
+ fns = vec_create(sizeof(struct fn));
+
+ struct fn main = {.name = "main",
+ .idx = 0,
+ .sp = 0,
+ .insns = vec_create(sizeof(struct insn))};
+
+ vect_append(struct fn, fns, &main);
+
+ foreach_node(n, tree) {
+ switch (n->k) {
+ case AST_PROC_DEF: add_proc(n); break;
+ case AST_FUNC_DEF: add_func(n); break;
+ default:
+ }
+ }
+
+ struct fn *f = &vect_at(struct fn, fns, 0);
+ foreach_node(n, tree) {
+ switch (n->k) {
+ case AST_VAR_DEF: lower_global_var(f, n); break;
+ case AST_PROC_DEF: lower_proc_def(n); break;
+ case AST_FUNC_DEF: lower_func_def(n); break;
+ default: lower(f, n);
+ }
+ }
+
+ output_insn(f, STOP, null_loc(), null_loc(), null_loc(), 0);
+
+#ifdef DEBUG
+ foreach_vec(fi, fns) {
+ struct fn *f = vec_at(&fns, fi);
+ printf("// %s (%zd):\n", f->name, f->idx);
+ dump_insns(f);
+ }
+#endif
+ return 0;
+}
+
+static void destroy_fn(struct fn *f)
+{
+ vec_destroy(&f->insns);
+}
+
+void destroy_lowering()
+{
+ foreach_vec(fi, fns) {
+ struct fn *f = vec_at(&fns, fi);
+ destroy_fn(f);
+ }
+
+ vec_destroy(&fns);
+}
diff --git a/src/parser.y b/src/parser.y
index 3b69b06..7aacd39 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -7,6 +7,7 @@
#include <posthaste/parser.h>
#include <posthaste/date.h>
+#include <posthaste/ast.h>
#define FOREACH_TOKEN(M) \
M(LPAREN) \
@@ -57,7 +58,7 @@
%parse-param {void *scanner} {struct parser* parser}
%union {
- struct ast_node *node;
+ struct ast *node;
ph_date_t num;
int64_t snum;
char *str;
@@ -101,6 +102,44 @@
%token PRINT "print"
%token END "end"
+%nterm <node> program
+%nterm <node> definition
+%nterm <node> definitions
+%nterm <node> opt_definitions
+%nterm <node> variable_definition
+%nterm <node> variable_definitions
+%nterm <node> opt_variable_definitions
+%nterm <node> procedure_definition
+%nterm <node> function_definition
+%nterm <node> formal_arg
+%nterm <node> formals
+%nterm <node> lvalue
+%nterm <node> rvalue
+%nterm <node> print_item
+%nterm <node> print_items
+%nterm <node> opt_formals
+%nterm <node> statement
+%nterm <node> statement_list
+%nterm <node> opt_arguments
+%nterm <node> arguments
+%nterm <node> print_statement
+%nterm <node> unless_statement
+%nterm <node> unless_expression
+%nterm <node> until_statement
+%nterm <node> assignment
+%nterm <node> procedure_call
+%nterm <node> function_call
+%nterm <node> simple_expr
+%nterm <node> atom
+%nterm <node> term
+%nterm <node> factor
+%nterm <node> expression
+%nterm <node> ident
+
+/* special case */
+%nterm <str> return
+%nterm <str> opt_return
+
%{
/** Modifies the signature of yylex to fit our parser better. */
@@ -145,11 +184,11 @@ static void yyerror(YYLTYPE *yylloc, void *lexer,
%%
statement_list
- : statement "," statement_list
+ : statement "," statement_list { $$ = $1; $$->n = $3; }
| statement
definitions
- : definition definitions
+ : definition definitions { $$ = $1; $$->n = $2; }
| definition
definition
@@ -158,74 +197,102 @@ definition
| variable_definition
variable_definition
- : "var" IDENT "=" expression
+ : "var" IDENT "=" expression {
+ $$ = gen_var_def($[IDENT], $[expression], src_loc(@$));
+ }
variable_definitions
- : variable_definition variable_definitions
+ : variable_definition variable_definitions { $$ = $1; $$->n = $2; }
| variable_definition
opt_variable_definitions
: variable_definitions
- |
+ | { $$ = NULL; }
return
- : "return" IDENT
+ : "return" IDENT { $$ = $2; }
opt_return
: return
- |
+ | { $$ = NULL; }
function_definition
: "function" FUNC_IDENT "{" opt_formals "}"
- return opt_variable_definitions "is" rvalue "end" "function"
+ return opt_variable_definitions "is" rvalue "end" "function" {
+ struct ast *ret = gen_return($[rvalue], $[rvalue]->loc);
+ $$ = gen_func_def($[FUNC_IDENT],
+ $[return],
+ $[opt_formals],
+ $[opt_variable_definitions],
+ ret,
+ src_loc(@$));
+ }
procedure_definition
: "procedure" PROC_IDENT "{" opt_formals "}"
opt_return opt_variable_definitions "is" statement_list
- "end" "procedure"
+ "end" "procedure" {
+ $$ = gen_proc_def($[PROC_IDENT],
+ $[opt_return],
+ $[opt_formals],
+ $[opt_variable_definitions],
+ $[statement_list],
+ src_loc(@$));
+ }
formals
- : formal_arg "," formal_arg
+ : formal_arg "," formals { $$ = $1; $$->n = $3; }
| formal_arg
opt_formals
: formals
- |
+ | { $$ = NULL; }
formal_arg
- : IDENT "[" IDENT "]"
+ : IDENT "[" IDENT "]" {
+ $$ = gen_formal_def($1, $3, src_loc(@$));
+ }
procedure_call
- : PROC_IDENT "(" opt_arguments ")"
+ : PROC_IDENT "(" opt_arguments ")" {
+ $$ = gen_proc_call($[PROC_IDENT], $[opt_arguments], src_loc(@$));
+ }
arguments
- : expression "," arguments
+ : expression "," arguments { $$ = $1; $$->n = $3; }
| expression
opt_arguments
: arguments
- |
+ | { $$ = NULL; }
assignment
- : lvalue "=" rvalue
+ : lvalue "=" rvalue {
+ $$ = gen_assign($[lvalue], $[rvalue], src_loc(@$));
+ }
+
+ident
+ : IDENT {
+ $$ = gen_id($[IDENT], src_loc(@$));
+ }
lvalue
- : IDENT
- | IDENT "." IDENT
+ : ident
+ | ident "." IDENT { $$ = gen_dot($1, $3, src_loc(@$)); }
rvalue
: expression
| unless_expression
print_statement
- : "print" print_items
+ : "print" print_items { $$ = gen_print($[print_items], src_loc(@$)); }
print_items
- : print_item "&" print_items
+ : print_item "&" print_items { $$ = $1; $$->n = $3; }
| print_item
print_item
- : STRING
+ : STRING { $$ = gen_const_string($[STRING], src_loc(@$)); }
| expression
statement
@@ -234,56 +301,98 @@ statement
| print_statement
| until_statement
| unless_statement
- | "return" expression
+ | "return" expression { $$ = gen_return($[expression], src_loc(@$)); }
until_statement
- : "do" statement_list "until" expression
+ : "do" statement_list "until" expression {
+ $$ = gen_until($[statement_list], $[expression], src_loc(@$));
+ }
unless_statement
- : "do" statement_list "unless" expression "done"
- | "do" statement_list "unless" expression "otherwise" statement_list "done"
+ : "do" statement_list "unless" expression "done" {
+ $$ = gen_unless($[statement_list], $[expression], NULL, src_loc(@$));
+ }
+ | "do" statement_list "unless" expression "otherwise" statement_list "done" {
+ $$ = gen_unless($2, $[expression], $6, src_loc(@$));
+ }
expression
: simple_expr
- | expression "=" simple_expr
- | expression "<" simple_expr
+ | expression "=" simple_expr {
+ $$ = gen_eq($1, $[simple_expr], src_loc(@$));
+ }
+ | expression "<" simple_expr {
+ $$ = gen_lt($1, $[simple_expr], src_loc(@$));
+ }
simple_expr
: term
- | simple_expr "+" term
- | simple_expr "-" term
+ | simple_expr "+" term {
+ $$ = gen_add($1, $[term], src_loc(@$));
+ }
+ | simple_expr "-" term {
+ $$ = gen_sub($1, $[term], src_loc(@$));
+ }
term
: factor
- | term "*" factor
- | term "/" factor
+ | term "*" factor {
+ $$ = gen_mul($1, $[factor], src_loc(@$));
+ }
+ | term "/" factor {
+ $$ = gen_div($1, $[factor], src_loc(@$));
+ }
factor
- : "+" atom
- | "-" atom
+ : "+" atom {
+ $$ = gen_pos($[atom], src_loc(@$));
+ }
+ | "-" atom {
+ $$ = gen_neg($[atom], src_loc(@$));
+ }
| atom
atom
- : IDENT
- | IDENT "'" IDENT
- | INT_LITERAL
- | DATE_LITERAL
+ : ident
+ | ident "'" IDENT {
+ $$ = gen_attr($[ident], $[IDENT], src_loc(@$));
+ }
+ | INT_LITERAL {
+ $$ = gen_const_int($[INT_LITERAL], src_loc(@$));
+ }
+ | DATE_LITERAL {
+ $$ = gen_const_date($[DATE_LITERAL], src_loc(@$));
+ }
| function_call
| procedure_call
- | "(" expression ")"
+ | "(" expression ")" { $$ = $2; }
function_call
- : FUNC_IDENT "(" opt_arguments ")"
+ : FUNC_IDENT "(" opt_arguments ")" {
+ $$ = gen_func_call($[FUNC_IDENT], $[opt_arguments], src_loc(@$));
+ }
unless_expression
- : "do" expression "unless" expression "otherwise" expression "done"
+ : "do" expression "unless" expression "otherwise" expression "done" {
+ $$ = gen_unless_expr($2, $4, $6, src_loc(@$));
+ }
opt_definitions
: definitions
- | {}
+ | { $$ = NULL; }
program
- : opt_definitions statement_list
+ : opt_definitions statement_list {
+ if ($1) {
+ ast_last($1)->n = $2;
+ $$ = $1;
+ }
+ else {
+ $$ = $2;
+ }
+
+ parser->tree = $$;
+ }
| error {
/* make sure to catch parse errors */
parser->failed = true;
@@ -293,44 +402,6 @@ program
#include "gen_lexer.inc"
-static void dump_yychar(struct parser *p, int yychar, YYSTYPE yylval, YYLTYPE yylloc)
-{
- struct src_loc loc = src_loc(yylloc);
- printf("%s:%d:%d: ", p->fname, loc.first_line, loc.first_col);
-
-#define PRINT_NAME(token) case token: printf(#token " "); break;
- switch (yychar) {
- FOREACH_TOKEN(PRINT_NAME);
- default: printf("Unknown yychar\n"); return;
- }
-
- char date_str[11] = {0};
- switch (yychar) {
- case INT_LITERAL: printf("(%lld)", (long long int)yylval.snum); break;
- case IDENT: printf("(%s)", yylval.str); break;
- case FUNC_IDENT: printf("(%s)", yylval.str); break;
- case PROC_IDENT: printf("(%s)", yylval.str); break;
- case DATE_LITERAL:
- date_to_string(date_str, yylval.num);
- printf("(%s)", date_str);
- break;
- }
-
- printf("\n");
-}
-
-static void dump_lex(struct parser *p)
-{
- int yychar;
- YYSTYPE yylval;
- YYLTYPE yylloc = {1, 1, 1, 1};
-
- /* run lexer until we reach the end of the file */
- while ((yychar = yylex(&yylval, &yylloc, p->lexer, p)) != YYEOF) {
- dump_yychar(p, yychar, yylval, yylloc);
- }
-}
-
static struct src_loc src_loc(YYLTYPE yylloc)
{
struct src_loc loc;
@@ -377,11 +448,5 @@ void parse(struct parser *p, const char *fname, const char *buf)
yylex_init(&p->lexer);
yy_scan_string(buf, p->lexer);
-
- // debugging, remember to reset yy_scan_string once the actual parser
- // runs
- dump_lex(p);
-
- yy_scan_string(buf, p->lexer);
yyparse(p->lexer, p);
}
diff --git a/src/scope.c b/src/scope.c
new file mode 100644
index 0000000..290dfd6
--- /dev/null
+++ b/src/scope.c
@@ -0,0 +1,153 @@
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <posthaste/scope.h>
+#include <posthaste/debug.h>
+#include <posthaste/vec.h>
+
+struct scope {
+ size_t id;
+ struct scope *parent;
+
+ const char *fname;
+ const char *buf;
+
+ struct vec visible;
+};
+
+struct vec scopes = {0};
+
+struct scope *create_scope()
+{
+ static size_t counter = 0;
+ if (vec_uninit(scopes)) {
+ scopes = vec_create(sizeof(struct scope *));
+ }
+
+ struct scope *scope = calloc(1, sizeof(struct scope));
+ if (!scope)
+ return NULL;
+
+ scope->visible = vec_create(sizeof(struct ast *));
+ scope->id = counter++;
+
+ vect_append(struct scope *, scopes, &scope);
+ return scope;
+}
+
+void scope_add_scope(struct scope *parent, struct scope *scope)
+{
+ scope->parent = parent;
+ scope->fname = parent->fname;
+ scope->buf = parent->buf;
+}
+
+void scope_set_file(struct scope *scope, const char *fname, const char *buf)
+{
+ scope->fname = fname;
+ scope->buf = buf;
+}
+
+struct ast *scope_find(struct scope *scope, char *id)
+{
+ /* not particularly fast, but good enough for now */
+ foreach_vec(vi, scope->visible) {
+ struct ast *n = vect_at(struct ast *, scope->visible, vi);
+ if (same_id(n->id, id))
+ return n;
+ }
+
+ return NULL;
+}
+
+struct ast *file_scope_find(struct scope *scope, char *id)
+{
+ struct ast *exists = scope_find(scope, id);
+ if (exists)
+ return exists;
+
+ if (scope->parent)
+ return file_scope_find(scope->parent, id);
+
+ return NULL;
+}
+
+int scope_add_var(struct scope *scope, struct ast *var)
+{
+ struct ast *exists = scope_find(scope, var_id(var));
+ if (exists) {
+ semantic_error(scope, var, "var redefined");
+ semantic_error(scope, exists, "previously here");
+ return -1;
+ }
+
+ vect_append(struct ast *, scope->visible, &var);
+ return 0;
+}
+
+int scope_add_formal(struct scope *scope, struct ast *formal)
+{
+ struct ast *exists = scope_find(scope, formal_id(formal));
+ if (exists) {
+ semantic_error(scope, formal, "formal redefined");
+ semantic_error(scope, exists, "previously here");
+ return -1;
+ }
+
+ vect_append(struct ast *, scope->visible, &formal);
+ return 0;
+}
+
+int scope_add_proc(struct scope *scope, struct ast *proc)
+{
+ struct ast *exists = scope_find(scope, proc_id(proc));
+ if (exists) {
+ semantic_error(scope, proc, "proc redefined");
+ semantic_error(scope, exists, "previously here");
+ return -1;
+ }
+
+ vect_append(struct ast *, scope->visible, &proc);
+ return 0;
+}
+
+int scope_add_func(struct scope *scope, struct ast *func)
+{
+ struct ast *exists = scope_find(scope, func_id(func));
+ if (exists) {
+ semantic_error(scope, func, "func redefined");
+ semantic_error(scope, exists, "previously here");
+ return -1;
+ }
+
+ vect_append(struct ast *, scope->visible, &func);
+ return 0;
+}
+
+static void destroy_scope(struct scope *scope)
+{
+ vec_destroy(&scope->visible);
+ free(scope);
+}
+
+void destroy_scopes()
+{
+ foreach_vec(si, scopes) {
+ struct scope *s = vect_at(struct scope *, scopes, si);
+ destroy_scope(s);
+ }
+
+ vec_destroy(&scopes);
+}
+
+void semantic_error(struct scope *scope, struct ast *n, const char *msg, ...)
+{
+ va_list args;
+ va_start(args, msg);
+ struct src_issue issue;
+ issue.loc = n->loc;
+ issue.fname = scope->fname;
+ issue.buf = scope->buf;
+ vsrc_issue(issue, msg, args);
+ va_end(args);
+}
diff --git a/src/vec.c b/src/vec.c
new file mode 100644
index 0000000..3fc2d7d
--- /dev/null
+++ b/src/vec.c
@@ -0,0 +1,75 @@
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <posthaste/vec.h>
+
+struct vec vec_create(size_t ns)
+{
+ return (struct vec) {
+ .n = 0,
+ .s = 1,
+ .ns = ns,
+ .buf = malloc(ns),
+ };
+}
+
+size_t vec_len(struct vec *v)
+{
+ return v->n;
+}
+
+void *vec_at(struct vec *v, size_t i)
+{
+ assert(i < v->n && "out of vector bounds");
+ return v->buf + i * v->ns;
+}
+
+void *vec_back(struct vec *v)
+{
+ assert(v->n);
+ return v->buf + (v->n - 1) * v->ns;
+}
+
+void *vec_pop(struct vec *v)
+{
+ assert(v->n && "attempting to pop empty vector");
+ v->n--;
+ return v->buf + v->n * v->ns;
+}
+
+void vec_append(struct vec *v, void *n)
+{
+ v->n++;
+ if (v->n >= v->s) {
+ v->s *= 2;
+ v->buf = realloc(v->buf, v->s * v->ns);
+ }
+
+ void *p = vec_at(v, v->n - 1);
+ memcpy(p, n, v->ns);
+}
+
+void vec_reset(struct vec *v)
+{
+ v->n = 0;
+}
+
+void vec_reserve(struct vec *v, size_t n)
+{
+ if (v->n >= n)
+ return;
+
+ v->n = n;
+ v->s = n;
+ v->buf = realloc(v->buf, v->s * v->ns);
+}
+
+void vec_destroy(struct vec *v) {
+ free(v->buf);
+}
+
+void vec_sort(struct vec *v, vec_comp_t comp)
+{
+ qsort(v->buf, v->n, v->ns, comp);
+}