├── .gitignore ├── Makefile ├── README.md ├── main.c ├── node.h ├── parse.y └── ruby.l /.gitignore: -------------------------------------------------------------------------------- 1 | program.rb 2 | lex.yy.c 3 | ruby 4 | parse.tab.c 5 | parse.tab.h 6 | parse.output 7 | ruby.dSYM 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRC=main.c parse.tab.c lex.yy.c 2 | 3 | all: ruby 4 | 5 | ruby: ${SRC} 6 | cc -O0 -g -o ruby ${SRC} 7 | 8 | lex.yy.c: ruby.l 9 | flex ruby.l 10 | 11 | parse.tab.c: parse.y 12 | bison -v -d parse.y 13 | 14 | clean: 15 | rm -rf ruby lex.yy.c parse.tab.c parse.tab.h parse.output 16 | 17 | check: 18 | docker run -v `pwd`:/usr/src/ruby ruby bash -c "make clean && make && (valgrind --leak-check=full --show-reachable=yes ./ruby program.rb)" 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reconstructing Ruby 2 | 3 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/halogenandtoast/reconstructing-ruby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | This repository represents the work being done via the blog series published 6 | on [halogenandtoast.com](http://halogenandtoast.com). The intent is to write 7 | a fully working ruby implementation, from scratch, using C. 8 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "node.h" 5 | #include "parse.tab.h" 6 | 7 | extern FILE* yyin; 8 | extern int yyparse(parser_state*); 9 | extern int yylex_destroy(void); 10 | 11 | int main(int argc, char *argv[]) { 12 | parser_state state = { NULL, 0 }; 13 | 14 | if(argc > 1) { 15 | state.source_file = argv[1]; 16 | yyin = fopen(argv[1], "r"); 17 | } 18 | 19 | yyparse(&state); 20 | 21 | if(argc > 1) { 22 | fclose(yyin); 23 | } 24 | 25 | yylex_destroy(); 26 | return EXIT_SUCCESS; 27 | } 28 | -------------------------------------------------------------------------------- /node.h: -------------------------------------------------------------------------------- 1 | #ifndef _NODE_H_ 2 | #define _NODE_H_ 3 | 4 | typedef struct parser_state { 5 | char *source_file; 6 | int source_line; 7 | } parser_state; 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /parse.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include "node.h" 4 | extern void yyerror(struct parser_state *state, const char *s); 5 | %} 6 | 7 | %pure-parser 8 | %parse-param { parser_state *state } 9 | %lex-param { state } 10 | 11 | %union { 12 | int ival; 13 | float fval; 14 | char *sval; 15 | } 16 | 17 | %{ 18 | extern int yylex(YYSTYPE *yylval, parser_state *state); 19 | %} 20 | 21 | 22 | %left tPLUS 23 | %right tEQUAL 24 | 25 | %token tFLOAT tNUMBER tEQUAL tGT tLT tGTE tLTE tNEQUAL 26 | %token tPLUS tMINUS tMULT tDIV tMOD tEMARK tQMARK tAND tOR tLSBRACE tRSBRACE 27 | %token tLPAREN tRPAREN tLBRACE tRBRACE tAT tDOT tCOMMA tCOLON 28 | %token kCLASS kEND kDEF 29 | %token tSTRING tCONSTANT tID 30 | 31 | %start program 32 | 33 | %% 34 | 35 | program: expressions { printf("%s\n", state->source_file); } 36 | 37 | expressions: expressions expression 38 | | expression 39 | 40 | expression: class_definition 41 | | binary_expression 42 | | method_definition 43 | | variable 44 | | assignment 45 | | method_call 46 | | value 47 | 48 | binary_expression: expression tPLUS expression 49 | 50 | assignment: variable tEQUAL expression 51 | 52 | class_definition: kCLASS tCONSTANT expressions kEND { free($2); } 53 | 54 | method_definition: kDEF tID expressions kEND { free($2); } 55 | | kDEF tID tLPAREN tID tRPAREN expressions kEND { free($2); free($4); } 56 | 57 | method_call: variable tDOT tID { free($3); } 58 | | tCONSTANT tDOT tID tLPAREN tSTRING tRPAREN { free($1); free($3); free($5); } 59 | | tID tSTRING { free($1); free($2); } 60 | 61 | variable: tID { free($1); } 62 | | tAT tID { free($2); } 63 | 64 | value: tNUMBER 65 | -------------------------------------------------------------------------------- /ruby.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | #include "node.h" 5 | #include "parse.tab.h" 6 | #define YY_DECL int yylex(YYSTYPE *yylval, parser_state *state) 7 | #define TOKEN(id) return t##id 8 | #define KEYWORD(id) return k##id 9 | %} 10 | 11 | %option noyywrap 12 | %option yylineno 13 | 14 | NUMBER [0-9](_[0-9]|[0-9])* 15 | 16 | %% 17 | class { KEYWORD(CLASS); } 18 | end { KEYWORD(END); } 19 | def {KEYWORD(DEF); } 20 | #.*$ {} 21 | \"([^"]|\\.)*\" { yylval->sval = strdup(yytext); TOKEN(STRING); } 22 | \'([^']|\\.)*\' { yylval->sval = strdup(yytext); TOKEN(STRING); } 23 | {NUMBER}(\.{NUMBER}|(\.{NUMBER})?[eE][+-]?{NUMBER}) { yylval->fval = atof(yytext); TOKEN(FLOAT); } 24 | {NUMBER} { yylval->ival = atoi(yytext); TOKEN(NUMBER); } 25 | [a-z_][a-zA-Z0-9_]* { yylval->sval = strdup(yytext); TOKEN(ID); } 26 | [A-Z][a-zA-Z0-9_]* { yylval->sval = strdup(yytext); TOKEN(CONSTANT); } 27 | "=" { TOKEN(EQUAL); } 28 | ">" { TOKEN(GT); } 29 | "<" { TOKEN(LT); } 30 | ">=" { TOKEN(GTE); } 31 | "<=" { TOKEN(LTE); } 32 | "!=" { TOKEN(NEQUAL); } 33 | "+" { TOKEN(PLUS); } 34 | "-" { TOKEN(MINUS); } 35 | "*" { TOKEN(MULT); } 36 | "/" { TOKEN(DIV); } 37 | "%" { TOKEN(MOD); } 38 | "!" { TOKEN(EMARK); } 39 | "?" { TOKEN(QMARK); } 40 | "&" { TOKEN(AND); } 41 | "|" { TOKEN(OR); } 42 | "[" { TOKEN(LSBRACE); } 43 | "]" { TOKEN(RSBRACE); } 44 | "(" { TOKEN(LPAREN); } 45 | ")" { TOKEN(RPAREN); } 46 | "{" { TOKEN(LBRACE); } 47 | "}" { TOKEN(RBRACE); } 48 | "@" { TOKEN(AT); } 49 | "." { TOKEN(DOT); } 50 | "," { TOKEN(COMMA); } 51 | ":" { TOKEN(COLON); } 52 | [\t ] {} 53 | \n {} 54 | . { fprintf(stderr, "Unknown token '%s'\n", yytext); } 55 | %% 56 | 57 | void yyerror(char const *s) { 58 | fprintf(stderr, "%s. Unexpected \"%s\" on line %d\n", s, yytext, yylineno); 59 | } 60 | --------------------------------------------------------------------------------