From 9de1dd73c4bebe2b3992f12f99d69dd56b6f53cf Mon Sep 17 00:00:00 2001
From: Recolic Keghart <root@recolic.net>
Date: Sat, 14 Dec 2019 23:53:05 +0800
Subject: [PATCH] >  Manual commit:  Finished expression parser. Slightly
 changed Makefile and gitignore file. U201614531 recolic Linux RECOLICPC
 5.4.2-arch1-1 #1 SMP PREEMPT Thu, 05 Dec 2019 12:29:40 +0000 x86_64 GNU/Linux
  23:53:05 up 3 days,  7:11,  1 user,  load average: 0.74, 1.27, 1.12
 31989000402905faaa2cb728bf473984cea394e

---
 .gitignore                                    |   9 ++
 nemu/Makefile                                 |  10 +-
 nemu/include/macro.h                          |  10 +-
 .../monitor/expr_impl/parser_interface.h      |  10 ++
 nemu/src/monitor/debug/expr.cc                |  18 ++-
 nemu/src/monitor/debug/expr_impl/Makefile     |  17 +++
 nemu/src/monitor/debug/expr_impl/lexer.l      |  24 ++++
 nemu/src/monitor/debug/expr_impl/parser.y     | 112 ++++++++++++++++++
 8 files changed, 195 insertions(+), 15 deletions(-)
 create mode 100644 nemu/include/monitor/expr_impl/parser_interface.h
 create mode 100644 nemu/src/monitor/debug/expr_impl/Makefile
 create mode 100644 nemu/src/monitor/debug/expr_impl/lexer.l
 create mode 100644 nemu/src/monitor/debug/expr_impl/parser.y

diff --git a/.gitignore b/.gitignore
index 420fc9c..671aff9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,13 @@
+*.yy.cc
+*.tab.cc
+*.tab.h
+
 *.d
 *.o
 *.out
+*.output
+
 build
+
+.idea
+.vscode
diff --git a/nemu/Makefile b/nemu/Makefile
index 4517522..6c8e56e 100644
--- a/nemu/Makefile
+++ b/nemu/Makefile
@@ -36,10 +36,14 @@ $(OBJ_DIR)/%.o: src/%.cc
 # Depencies
 -include $(OBJS:.o=.d)
 
-# Some convinient rules
+# Source code generation before any targets.
+SUBDIRS = src/monitor/debug/expr_impl
+$(SUBDIRS):
+	$(MAKE) -C $@
 
-.PHONY: app run clean
-app: $(BINARY)
+# Some convinient rules
+.PHONY: app run clean $(SUBDIRS)
+app: $(SUBDIRS) $(BINARY)
 
 override ARGS ?= -l $(BUILD_DIR)/nemu-log.txt
 override ARGS += -d $(NEMU_HOME)/tools/qemu-diff/build/qemu-so
diff --git a/nemu/include/macro.h b/nemu/include/macro.h
index 2026391..80a72cc 100644
--- a/nemu/include/macro.h
+++ b/nemu/include/macro.h
@@ -1,14 +1,8 @@
 #ifndef __MACRO_H__
 #define __MACRO_H__
 
-// From rlib/macro.hpp
-#ifndef _RLIB_MACRO_ENSTRING
-#define _RLIB_MACRO_ENSTRING(_s) #_s
-#endif
-
-#ifndef RLIB_MACRO_TO_CSTR
-#define RLIB_MACRO_TO_CSTR(m) _RLIB_MACRO_ENSTRING(m)
-#endif
+// Implements origin `str()` macro. Extremely bad naming.
+#include <rlib/macro.hpp>
 
 #define concat_temp(x, y) x ## y
 #define concat(x, y) concat_temp(x, y)
diff --git a/nemu/include/monitor/expr_impl/parser_interface.h b/nemu/include/monitor/expr_impl/parser_interface.h
new file mode 100644
index 0000000..f661006
--- /dev/null
+++ b/nemu/include/monitor/expr_impl/parser_interface.h
@@ -0,0 +1,10 @@
+#ifndef RCPP_HUST_PARSER_INTERFACE_H_
+#define RCPP_HUST_PARSER_INTERFACE_H_ 1
+
+#include <string>
+#include <cstdint>
+
+// Implemented in src/monitor/debug/expr_impl/parser.y
+int64_t parse_one(std::string expr);
+
+#endif
\ No newline at end of file
diff --git a/nemu/src/monitor/debug/expr.cc b/nemu/src/monitor/debug/expr.cc
index cc38022..4a09ceb 100644
--- a/nemu/src/monitor/debug/expr.cc
+++ b/nemu/src/monitor/debug/expr.cc
@@ -6,6 +6,9 @@
 #include <sys/types.h>
 #include <regex.h>
 
+#include "monitor/expr_impl/parser_interface.h"
+#include <rlib/stdio.hpp>
+
 enum {
   TK_NOTYPE = 256, TK_EQ
 
@@ -14,7 +17,7 @@ enum {
 };
 
 static struct rule {
-  char *regex;
+  const char *regex;
   int token_type;
 } rules[] = {
 
@@ -103,7 +106,14 @@ uint32_t expr(char *e, bool *success) {
   }
 
   /* TODO: Insert codes to evaluate the expression. */
-  TODO();
-
-  return 0;
+  try {
+    auto res = parse_one(std::string(e));
+    *success = true;
+    return res;
+  }
+  catch(std::exception &e) {
+    rlib::println("Exception caught:", e.what());
+    *success = false;
+    return 0;
+  }
 }
diff --git a/nemu/src/monitor/debug/expr_impl/Makefile b/nemu/src/monitor/debug/expr_impl/Makefile
new file mode 100644
index 0000000..71d762f
--- /dev/null
+++ b/nemu/src/monitor/debug/expr_impl/Makefile
@@ -0,0 +1,17 @@
+generate: parser.tab.cc lexer.yy.cc parser.tab.h
+
+all: parser
+
+parser.tab.cc parser.tab.h:	parser.y
+	@echo Generating $@...
+	bison --defines=parser.tab.h -o parser.tab.cc parser.y
+
+lexer.yy.cc: lexer.l parser.tab.h
+	@echo Generating $@...
+	flex -o lexer.yy.cc lexer.l
+
+parser: generate
+	g++ -o parser parser.tab.cc lexer.yy.cc
+
+clean:
+	rm -f parser parser.tab.cc lexer.yy.cc parser.tab.h
diff --git a/nemu/src/monitor/debug/expr_impl/lexer.l b/nemu/src/monitor/debug/expr_impl/lexer.l
new file mode 100644
index 0000000..7da3a97
--- /dev/null
+++ b/nemu/src/monitor/debug/expr_impl/lexer.l
@@ -0,0 +1,24 @@
+%option noyywrap
+
+%{
+
+#define YY_DECL int yylex()
+
+#include "parser.tab.h"
+#include <stdint.h>
+
+%}
+
+%%
+
+[ \t\n]       {}
+0x[0-9]+      {yylval.ival = (int)strtol(yytext, NULL, 16); return T_INT;}
+[0-9]+        {yylval.ival = atoi(yytext); return T_INT;}
+"=="          {return T_EQUAL;}
+"!="          {return T_NEQUAL;}
+"&&"          {return T_LOGICAL_AND;}
+[-+*/()]      {return yytext[0];}
+
+"%"[a-z]{3}   {yylval.ival = ((uint32_t)yytext[1] << 8) + yytext[2]; return T_REG;}
+%%
+// Registers: eax='a'+'x', ebx='b'+'x', ...
diff --git a/nemu/src/monitor/debug/expr_impl/parser.y b/nemu/src/monitor/debug/expr_impl/parser.y
new file mode 100644
index 0000000..f53b0b5
--- /dev/null
+++ b/nemu/src/monitor/debug/expr_impl/parser.y
@@ -0,0 +1,112 @@
+%{
+
+#include <string>
+#include <rlib/stdio.hpp>
+#include <cassert>
+#include <cstdlib>
+
+extern int yylex();
+extern int yyparse();
+
+typedef struct yy_buffer_state * YY_BUFFER_STATE;
+extern YY_BUFFER_STATE yy_scan_string(const char * str);
+void yyerror(int64_t *result, std::string s);
+
+uint32_t ryy_read_cpu_reg_value(int64_t which_reg);
+uint32_t ryy_read_memory_value(uint32_t addr);
+%}
+
+%union {
+    int64_t ival;
+}
+
+%token<ival> T_INT T_REG // I represent REG by: (int)(REG_NAME[1] << 8 + REG_NAME[2])
+%left T_LOGICAL_AND
+%left T_EQUAL T_NEQUAL
+%left '+' '-'
+%left '*' '/'
+
+%type<ival> expr
+
+%start entry
+
+%parse-param {int64_t *result}
+
+%%
+entry:                          {*result = 0;}
+      | expr                    {*result = $1;}
+;
+
+expr: T_INT                     { $$ = $1; }
+      | expr '+' expr           { $$ = $1 + $3; }
+      | expr '-' expr           { $$ = $1 - $3; }
+      | expr '*' expr           { $$ = $1 * $3; }
+      | expr '/' expr           { $$ = $1 / $3; }
+      | expr T_EQUAL expr       { $$ = ($1 == $3); }
+      | expr T_NEQUAL expr      { $$ = ($1 != $3); }
+      | expr T_LOGICAL_AND expr { $$ = ($1 && $3); }
+      | '(' expr ')'            { $$ = $2; }
+      | '-' expr                { $$ = 0 - $2; }
+      | '*' expr                { $$ = ryy_read_memory_value((uint32_t)$2); }
+      | T_REG                   { $$ = ryy_read_cpu_reg_value($1); }
+;
+
+%%
+
+#include "monitor/expr_impl/parser_interface.h"
+
+int64_t parse_one(std::string expr) {
+    yy_scan_string(expr.c_str());
+    int64_t result;
+    assert(0 == yyparse(&result));
+    return result;
+}
+
+int ryy_test_main() {
+    while(!std::cin.eof()) {
+        rlib::println(parse_one(rlib::scanln()));
+    }
+    return 0;
+}
+
+void yyerror(int64_t *result, std::string msg) {
+    rlib::println("Parse error:", msg);
+    exit(1);
+}
+
+// v-cpu related code:
+
+#include "cpu/reg.h"
+#include "memory/memory.h"
+#include <rlib/macro.hpp>
+
+uint32_t ryy_read_cpu_reg_value(int64_t which_reg) {
+#define CODE_OF(reg) (((uint32_t)RLIB_MACRO_TO_CSTR(reg)[1] << 8) + RLIB_MACRO_TO_CSTR(reg)[2])
+    switch(which_reg) {
+        case CODE_OF(eax):
+            return cpu.eax;
+        case CODE_OF(ebx):
+            return cpu.ebx;
+        case CODE_OF(ecx):
+            return cpu.ecx;
+        case CODE_OF(edx):
+            return cpu.edx;
+        case CODE_OF(esp):
+            return cpu.esp;
+        case CODE_OF(ebp):
+            return cpu.ebp;
+        case CODE_OF(esi):
+            return cpu.esi;
+        case CODE_OF(edi):
+            return cpu.edi;
+        case CODE_OF(eip):
+            return cpu.eip;
+        default:
+            throw std::invalid_argument("Invalid register name.");
+    }
+#undef CODE_OF
+}
+
+uint32_t ryy_read_memory_value(uint32_t addr) {
+    return vaddr_read(addr, 4);
+}
-- 
GitLab