diff --git a/CMakeLists.txt b/CMakeLists.txt index 073f507..e0abd24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,5 +20,8 @@ target_link_libraries(calc2 pthread) add_executable(calc3 example/calc3.cc) target_link_libraries(calc3 pthread) +add_executable(pl0 example/pl0.cc) +target_link_libraries(pl0 pthread) + add_executable(culebra language/main.cc) target_link_libraries(culebra pthread) diff --git a/example/Makefile b/example/Makefile index 10237a9..9daa22e 100644 --- a/example/Makefile +++ b/example/Makefile @@ -19,3 +19,6 @@ calc2 : calc2.cc ../peglib.h calc3 : calc3.cc ../peglib.h $(CC) -o calc3 $(CFLAGS) -I.. calc3.cc + +pl0 : pl0.cc ../peglib.h + $(CC) -o pl0 $(CFLAGS) -I.. pl0.cc diff --git a/example/example.sln b/example/example.sln index f88ef2b..4b46397 100644 --- a/example/example.sln +++ b/example/example.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "calc", "calc.vcxproj", "{F85B641A-7538-4809-8175-C528FF632CF6}" EndProject @@ -9,6 +9,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "calc2", "calc2.vcxproj", "{ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "calc3", "calc3.vcxproj", "{E6146F73-3B4C-4D4C-BC55-148930954434}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pl0", "pl0.vcxproj", "{6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -27,6 +29,10 @@ Global {E6146F73-3B4C-4D4C-BC55-148930954434}.Debug|Win32.Build.0 = Debug|Win32 {E6146F73-3B4C-4D4C-BC55-148930954434}.Release|Win32.ActiveCfg = Release|Win32 {E6146F73-3B4C-4D4C-BC55-148930954434}.Release|Win32.Build.0 = Release|Win32 + {6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}.Debug|Win32.ActiveCfg = Debug|Win32 + {6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}.Debug|Win32.Build.0 = Debug|Win32 + {6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}.Release|Win32.ActiveCfg = Release|Win32 + {6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/example/pl0.cc b/example/pl0.cc new file mode 100644 index 0000000..e94ae21 --- /dev/null +++ b/example/pl0.cc @@ -0,0 +1,353 @@ +// +// pl0.cc - PL/0 interpreter (https://en.wikipedia.org/wiki/PL/0) +// +// Copyright (c) 2015 Yuji Hirose. All rights reserved. +// MIT License +// + +#include +#include +#include + +using namespace peglib; +using namespace std; + +auto grammar = R"( + program <- _ block '.' _ + + block <- const var procedure statement + const <- ('CONST' _ ident '=' _ number (',' _ ident '=' _ number)* ';' _)? + var <- ('VAR' _ ident (',' _ ident)* ';' _)? + procedure <- ('PROCEDURE' _ ident ';' _ block ';' _)* + + statement <- (assignment / call / statements / if / while / out / in)? + assignment <- ident ':=' _ expression + call <- 'CALL' _ ident + statements <- 'BEGIN' _ statement (';' _ statement )* 'END' _ + if <- 'IF' _ condition 'THEN' _ statement + while <- 'WHILE' _ condition 'DO' _ statement + out <- ('out' / 'write' / '!') _ expression + in <- ('in' / 'read' / '?') _ ident + + condition <- odd / compare + odd <- 'ODD' _ expression + compare <- expression compare_op expression + compare_op <- < '=' / '#' / '<=' / '<' / '>=' / '>' > _ + + expression <- sign term (term_op term)* + sign <- < [-+]? > _ + term_op <- < [-+] > _ + + term <- factor (factor_op factor)* + factor_op <- < [*/] > _ + + factor <- ident / number / '(' _ expression ')' _ + + ident <- < [a-z] ([a-z] / [0-9])* > _ + number <- < [0-9]+ > _ + + ~_ <- [ \t\r\n]* +)"; + +struct Environment +{ + Environment(shared_ptr outer = nullptr) : outer(outer) {} + + int get_value(const string& ident) const { + try { + return get_constant(ident); + } catch (...) { + return get_variable(ident); + } + } + + void set_variable(const string& ident, int val) { + if (variables.find(ident) != variables.end()) { + variables[ident] = val; + } else if (outer) { + return outer->set_variable(ident, val); + } else { + throw runtime_error("undefined variable"); + } + } + + int get_constant(const string& ident) const { + if (constants.find(ident) != constants.end()) { + return constants.at(ident); + } else if (outer) { + return outer->get_constant(ident); + } else { + throw runtime_error("undefined constants"); + } + } + + int get_variable(const string& ident) const { + if (variables.find(ident) != variables.end()) { + return variables.at(ident); + } else if (outer) { + return outer->get_variable(ident); + } else { + throw runtime_error("undefined variable"); + } + } + + map constants; + map variables; + map> procedures; + shared_ptr outer; +}; + +struct Interpreter +{ + void exec(const shared_ptr ast, shared_ptr env) { + switch (ast->tag) { + case "block"_: exec_block(ast, env); break; + case "statement"_: exec_statement(ast, env); break; + case "assignment"_: exec_assignment(ast, env); break; + case "call"_: exec_call(ast, env); break; + case "statements"_: exec_statements(ast, env); break; + case "if"_: exec_if(ast, env); break; + case "while"_: exec_while(ast, env); break; + case "out"_: exec_out(ast, env); break; + case "in"_: exec_in(ast, env); break; + default: throw logic_error("invalid Ast type"); + } + } + +private: + void exec_block(const shared_ptr ast, shared_ptr outer) { + // block <- const var procedure statement + auto env = make_shared(outer); + const auto& nodes = ast->nodes; + exec_constants(nodes[0], env); + exec_variables(nodes[1], env); + exec_procedures(nodes[2], env); + exec(nodes[3], env); + } + + void exec_constants(const shared_ptr ast, shared_ptr env) { + // const <- ('CONST' _ ident '=' _ number(',' _ ident '=' _ number)* ';' _) ? + const auto& nodes = ast->nodes; + for (auto i = 0u; i < nodes.size(); i += 2) { + const auto& ident = nodes[i + 0]->token; + auto number = stoi(nodes[i + 1]->token); + env->constants[ident] = number; + } + } + + void exec_variables(const shared_ptr ast, shared_ptr env) { + // var <- ('VAR' _ ident(',' _ ident)* ';' _) ? + const auto& nodes = ast->nodes; + for (auto i = 0u; i < nodes.size(); i += 1) { + const auto& ident = nodes[i]->token; + env->variables[ident] = 0; + } + } + + void exec_procedures(const shared_ptr ast, shared_ptr env) { + // procedure <- ('PROCEDURE' _ ident ';' _ block ';' _)* + const auto& nodes = ast->nodes; + for (auto i = 0u; i < nodes.size(); i += 2) { + const auto& ident = nodes[i + 0]->token; + auto block = nodes[i + 1]; + env->procedures[ident] = block; + } + } + + void exec_statement(const shared_ptr ast, shared_ptr env) { + // statement <-(assignment / call / statements / if / while / out / in) ? + if (!ast->nodes.empty()) { + exec(ast->nodes[0], env); + } + } + + void exec_assignment(const shared_ptr ast, shared_ptr env) { + // assignment <- ident ':=' _ expression + const auto& ident = ast->nodes[0]->token; + auto val = eval(ast->nodes[1], env); + env->set_variable(ident, val); + } + + void exec_call(const shared_ptr ast, shared_ptr env) { + // call <- 'CALL' _ ident + const auto& ident = ast->nodes[0]->token; + auto proc = env->procedures[ident]; + exec_block(proc, env); + } + + void exec_statements(const shared_ptr ast, shared_ptr env) { + // statements <- 'BEGIN' _ statement (';' _ statement )* 'END' _ + for (auto stmt: ast->nodes) { + exec(stmt, env); + } + } + + void exec_if(const shared_ptr ast, shared_ptr env) { + // if <- 'IF' _ condition 'THEN' _ statement + auto cond = eval_condition(ast->nodes[0], env); + auto stmt = ast->nodes[1]; + if (cond) { + exec(stmt, env); + } + } + + void exec_while(const shared_ptr ast, shared_ptr env) { + // while <- 'WHILE' _ condition 'DO' _ statement + auto cond = ast->nodes[0]; + auto stmt = ast->nodes[1]; + auto ret = eval_condition(cond, env); + while (ret) { + exec(stmt, env); + ret = eval_condition(cond, env); + } + } + + void exec_out(const shared_ptr ast, shared_ptr env) { + // out <- '!' _ expression + auto val = eval(ast->nodes[0], env); + cout << val << endl; + } + + void exec_in(const shared_ptr ast, shared_ptr env) { + // in <- '?' _ ident + int val; + cin >> val; + const auto& ident = ast->nodes[0]->token; + env->variables[ident] = val; + } + + bool eval_condition(const shared_ptr ast, shared_ptr env) { + // condition <- odd / compare + const auto& node = ast->nodes[0]; + switch (node->tag) { + case "odd"_: return eval_odd(node, env); + case "compare"_: return eval_compare(node, env); + default: throw logic_error("invalid Ast type"); + } + } + + bool eval_odd(const shared_ptr ast, shared_ptr env) { + // odd <- 'ODD' _ expression + auto val = eval_expression(ast->nodes[0], env); + return val != 0; + } + + bool eval_compare(const shared_ptr ast, shared_ptr env) { + // compare <- expression compare_op expression + auto lval = eval_expression(ast->nodes[0], env); + auto op = peglib::str2tag(ast->nodes[1]->token.c_str()); + auto rval = eval_expression(ast->nodes[2], env); + switch (op) { + case "="_: return lval == rval; + case "#"_: return lval != rval; + case "<="_: return lval <= rval; + case "<"_: return lval < rval; + case ">="_: return lval >= rval; + case ">"_: return lval > rval; + } + } + + int eval(const shared_ptr ast, shared_ptr env) { + switch (ast->tag) { + case "expression"_: return eval_expression(ast, env); + case "term"_: return eval_term(ast, env); + case "ident"_: return eval_ident(ast, env); + case "number"_: return eval_number(ast, env); + default: throw logic_error("invalid Ast type"); + } + } + + int eval_expression(const shared_ptr ast, shared_ptr env) { + // expression <- sign term (term_op term)* + auto sign = ast->nodes[0]->token; + auto sign_val = (sign.empty() || sign == "+") ? 1 : -1; + auto val = eval(ast->nodes[1], env) * sign_val; + const auto& nodes = ast->nodes; + for (auto i = 2u; i < nodes.size(); i += 2) { + auto ope = nodes[i + 0]->token[0]; + auto rval = eval(nodes[i + 1], env); + switch (ope) { + case '+': val = val + rval; break; + case '-': val = val - rval; break; + } + } + return val; + } + + int eval_term(const shared_ptr ast, shared_ptr env) { + // term <- factor (factor_op factor)* + auto val = eval(ast->nodes[0], env); + const auto& nodes = ast->nodes; + for (auto i = 1u; i < nodes.size(); i += 2) { + auto ope = nodes[i + 0]->token[0]; + auto rval = eval(nodes[i + 1], env); + switch (ope) { + case '*': + val = val * rval; + break; + case '/': + if (rval == 0) { + throw runtime_error("divide by 0 error"); + } + val = val / rval; + break; + } + } + return val; + } + + int eval_ident(const shared_ptr ast, shared_ptr env) { + return env->get_value(ast->token); + } + + int eval_number(const shared_ptr ast, shared_ptr env) { + return stol(ast->token); + } +}; + +bool read_file(const char* path, vector& buff) +{ + ifstream ifs(path, ios::in | ios::binary); + if (ifs.fail()) { + return false; + } + buff.resize(static_cast(ifs.seekg(0, ios::end).tellg())); + if (!buff.empty()) { + ifs.seekg(0, ios::beg).read(&buff[0], static_cast(buff.size())); + } + return true; +} + +int main(int argc, const char** argv) +{ + if (argc < 2) { + cout << "usage: pl0 PATH [--ast]" << endl; + return 1; + } + + auto path = argv[1]; + vector source; + if (!read_file(path, source)) { + cerr << "can't open the source file." << endl; + return -1; + } + + peg parser(grammar); + parser.enable_ast(false, { "program", "statement", "statements", "term", "factor" }); + + shared_ptr ast; + if (parser.parse_n(source.data(), source.size(), ast, path)) { + if (argc > 2 && string("--ast") == argv[2]) { + ast->print(); + } + Interpreter interp; + auto env = make_shared(); + interp.exec(ast, env); + return 0; + } + + cout << "syntax error..." << endl; + return -1; +} + +// vim: et ts=4 sw=4 cin cino={1s ff=unix diff --git a/example/pl0.vcxproj b/example/pl0.vcxproj new file mode 100644 index 0000000..c746807 --- /dev/null +++ b/example/pl0.vcxproj @@ -0,0 +1,92 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + {6C5633BD-3CAE-498E-B0C6-ED90A1A99C47} + Win32Proj + sample + pl0 + + + + Application + true + Unicode + v140 + + + Application + false + true + Unicode + v140 + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + .. + + + Console + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + .. + + + Console + true + true + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file