Added language project.

pull/3/head
yhirose 9 years ago
parent 7ef79b62e2
commit 0168d19a0c
  1. 5
      language/CMakeLists.txt
  2. 26
      language/culebra.sln
  3. 97
      language/culebra.vcxproj
  4. 201
      language/interpreter.cc
  5. 196
      language/interpreter.hpp
  6. 1920
      language/linenoise.hpp
  7. 73
      language/main.cc
  8. 72
      language/parser.cc
  9. 11
      language/parser.hpp
  10. 31
      language/repl.cc
  11. 5
      language/repl.hpp

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.0)
include_directories(..)
add_definitions("-std=c++1y")
add_executable(culebra main.cc repl.cc interpreter.cc parser.cc)

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "culebra", "culebra.vcxproj", "{F85B641A-7538-4809-8175-C528FF632CF6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F85B641A-7538-4809-8175-C528FF632CF6}.Debug|Win32.ActiveCfg = Debug|Win32
{F85B641A-7538-4809-8175-C528FF632CF6}.Debug|Win32.Build.0 = Debug|Win32
{F85B641A-7538-4809-8175-C528FF632CF6}.Release|Win32.ActiveCfg = Release|Win32
{F85B641A-7538-4809-8175-C528FF632CF6}.Release|Win32.Build.0 = Release|Win32
{1D09607B-E1C0-4D62-8AB4-9E2D2C2DC6E4}.Debug|Win32.ActiveCfg = Debug|Win32
{1D09607B-E1C0-4D62-8AB4-9E2D2C2DC6E4}.Debug|Win32.Build.0 = Debug|Win32
{1D09607B-E1C0-4D62-8AB4-9E2D2C2DC6E4}.Release|Win32.ActiveCfg = Release|Win32
{1D09607B-E1C0-4D62-8AB4-9E2D2C2DC6E4}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\peglib.h" />
<ClInclude Include="interpreter.hpp" />
<ClInclude Include="parser.hpp" />
<ClInclude Include="repl.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="interpreter.cc" />
<ClCompile Include="main.cc" />
<ClCompile Include="parser.cc" />
<ClCompile Include="repl.cc" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{F85B641A-7538-4809-8175-C528FF632CF6}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<ProjectName>culebra</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,201 @@
#include "interpreter.hpp"
#include "parser.hpp"
using namespace peglib;
using namespace std;
struct Eval
{
static Value eval(const Ast& ast, Env& env) {
switch (ast.type) {
case Statements: return eval_statements(ast, env);
case While: return eval_while(ast, env);
case If: return eval_if(ast, env);
case Function: return eval_function(ast, env);
case FunctionCall: return eval_function_call(ast, env);
case Assignment: return eval_assignment(ast, env);
case Condition: return eval_condition(ast, env);
case BinExpresion: return eval_bin_expression(ast, env);
case Identifier: return eval_identifier(ast, env);
case Number: return eval_number(ast, env);
case Boolean: return eval_bool(ast, env);
}
if (ast.is_token) {
return Value(ast.token);
}
// NOTREACHED
throw logic_error("invalid Ast type");
}
private:
static Value eval_statements(const Ast& ast, Env& env) {
if (ast.is_token) {
return eval(ast, env);
} else if (ast.nodes.empty()) {
return Value();
}
auto it = ast.nodes.begin();
while (it != ast.nodes.end() - 1) {
eval(**it, env);
++it;
}
return eval(**it, env);
}
static Value eval_while(const Ast& ast, Env& env) {
for (;;) {
auto cond = eval(*ast.nodes[0], env);
if (!cond.to_bool()) {
break;
}
eval(*ast.nodes[1], env);
}
return Value();
}
static Value eval_if(const Ast& ast, Env& env) {
auto cond = eval(*ast.nodes[0], env);
if (cond.to_bool()) {
return eval(*ast.nodes[1], env);
} else if (ast.nodes.size() > 2) {
return eval(*ast.nodes[2], env);
}
return Value();
}
static Value eval_function(const Ast& ast, Env& env) {
vector<string> params;
for (auto node: ast.nodes[0]->nodes) {
params.push_back(node->token);
}
auto body = ast.nodes[1];
auto f = Value::FunctionValue {
params,
[=](Env& env) { return eval(*body, env); }
};
return Value(f);
};
static Value eval_function_call(const Ast& ast, Env& env) {
const auto& var = ast.nodes[0]->token;
const auto& args = ast.nodes[1]->nodes;
const auto& f = dereference_identirier(env, var);
const auto& fv = f.to_function();
if (fv.params.size() <= args.size()) {
Env callEnv(env);
callEnv.set("self", f);
for (auto i = 0u; i < fv.params.size(); i++) {
auto var = fv.params[i];
auto arg = args[i];
auto val = eval(*arg, env);
callEnv.set(var, val);
}
return fv.eval(callEnv);
}
string msg = "arguments error in '" + var + "'...";
throw runtime_error(msg);
}
static Value eval_condition(const Ast& ast, Env& env) {
auto lhs = eval(*ast.nodes[0], env);
if (ast.nodes.size() > 1) {
auto ope = eval(*ast.nodes[1], env).to_string();
auto rhs = eval(*ast.nodes[2], env);
if (ope == "==") {
return Value(lhs == rhs);
} else if (ope == "!=") {
return Value(lhs != rhs);
} else if (ope == "<=") {
return Value(lhs <= rhs);
} else if (ope == "<") {
return Value(lhs < rhs);
} else if (ope == ">=") {
return Value(lhs >= rhs);
} else if (ope == ">") {
return Value(lhs > rhs);
}
throw std::logic_error("invalid internal condition.");
}
return lhs; // Any
}
static Value eval_bin_expression(const Ast& ast, Env& env) {
auto ret = eval(*ast.nodes[0], env).to_long();
for (auto i = 1u; i < ast.nodes.size(); i += 2) {
auto val = eval(*ast.nodes[i + 1], env).to_long();
auto ope = eval(*ast.nodes[i], env).to_string()[0];
switch (ope) {
case '+': ret += val; break;
case '-': ret -= val; break;
case '*': ret *= val; break;
case '/': ret /= val; break;
}
}
return Value(ret);
}
static Value eval_assignment(const Ast& ast, Env& env) {
const auto& var = ast.nodes[0]->token;
auto val = eval(*ast.nodes[1], env);
env.set(var, val);
return val;
};
static Value eval_identifier(const Ast& ast, Env& env) {
const auto& var = ast.token;
return dereference_identirier(env, var);
};
static Value eval_number(const Ast& ast, Env& env) {
return Value(stol(ast.token));
};
static Value eval_bool(const Ast& ast, Env& env) {
return Value(ast.token == "true");
};
static Value dereference_identirier(Env& env, const string& var) {
if (!env.has(var)) {
string msg = "undefined variable '" + var + "'...";
throw runtime_error(msg);
}
return env.get(var);
};
};
std::ostream& operator<<(std::ostream& os, const Value& val)
{
return val.out(os);
}
bool run(Env& env, const char* expr, size_t len, Value& val, std::string& msg, bool print_ast)
{
try {
shared_ptr<Ast> ast;
if (get_parser().parse_n(expr, len, ast)) {
if (print_ast) {
ast->print();
}
val = Eval::eval(*ast, env);
return true;
}
} catch (exception& e) {
msg = e.what();
}
return false;
}
// vim: et ts=4 sw=4 cin cino={1s ff=unix

@ -0,0 +1,196 @@
#include <map>
#include <string>
#include <peglib.h>
struct Env;
struct Value
{
enum Type { Undefined, Bool, Long, String, Array, Function };
struct ArrayValue {
std::vector<Value> values;
};
struct FunctionValue {
std::vector<std::string> params;
std::function<Value (Env& env)> eval;
};
explicit Value() : type(Undefined) {}
explicit Value(bool b) : type(Bool) { v = b; }
explicit Value(long l) : type(Long) { v = l; }
explicit Value(const std::string& s) : type(String) { v = s; }
explicit Value(const ArrayValue& a) : type(Array) { v = a; }
explicit Value(const FunctionValue& f) : type(Function) { v = f; }
bool to_bool() const {
switch (type) {
case Bool: return v.get<bool>();
case Long: return v.get<long>() != 0;
}
throw std::runtime_error("type error.");
}
long to_long() const {
switch (type) {
case Bool: return v.get<bool>();
case Long: return v.get<long>();
}
throw std::runtime_error("type error.");
}
std::string to_string() const {
switch (type) {
case String: return v.get<std::string>();
}
throw std::runtime_error("type error.");
}
ArrayValue to_array() const {
switch (type) {
case Array: return v.get<ArrayValue>();
}
throw std::runtime_error("type error.");
}
FunctionValue to_function() const {
switch (type) {
case Function: return v.get<FunctionValue>();
}
throw std::runtime_error("type error.");
}
std::ostream& out(std::ostream& os) const {
switch (type) {
case Undefined: os << "undefined"; break;
case Bool: os << (to_bool() ? "true" : "false"); break;
case Long: os << to_long(); break;
case String: os << "'" << to_string() << "'"; break;
case Function: os << "[function]"; break;
case Array: os << "[array]"; break;
default: throw std::logic_error("invalid internal condition.");
}
return os;
}
bool operator==(const Value& rhs) const {
switch (type) {
case Undefined: return true;
case Bool: return to_bool() == rhs.to_bool();
case Long: return to_long() == rhs.to_long();
case String: return to_string() == rhs.to_string();
default: throw std::logic_error("invalid internal condition.");
}
// NOTREACHED
}
bool operator!=(const Value& rhs) const {
switch (type) {
case Undefined: return false;
case Bool: return to_bool() != rhs.to_bool();
case Long: return to_long() != rhs.to_long();
case String: return to_string() != rhs.to_string();
default: throw std::logic_error("invalid internal condition.");
}
// NOTREACHED
}
bool operator<=(const Value& rhs) const {
switch (type) {
case Undefined: return false;
case Bool: return to_bool() <= rhs.to_bool();
case Long: return to_long() <= rhs.to_long();
case String: return to_string() <= rhs.to_string();
default: throw std::logic_error("invalid internal condition.");
}
// NOTREACHED
}
bool operator<(const Value& rhs) const {
switch (type) {
case Undefined: return false;
case Bool: return to_bool() < rhs.to_bool();
case Long: return to_long() < rhs.to_long();
case String: return to_string() < rhs.to_string();
default: throw std::logic_error("invalid internal condition.");
}
// NOTREACHED
}
bool operator>=(const Value& rhs) const {
switch (type) {
case Undefined: return false;
case Bool: return to_bool() >= rhs.to_bool();
case Long: return to_long() >= rhs.to_long();
case String: return to_string() >= rhs.to_string();
default: throw std::logic_error("invalid internal condition.");
}
// NOTREACHED
}
bool operator>(const Value& rhs) const {
switch (type) {
case Undefined: return false;
case Bool: return to_bool() > rhs.to_bool();
case Long: return to_long() > rhs.to_long();
case String: return to_string() > rhs.to_string();
default: throw std::logic_error("invalid internal condition.");
}
// NOTREACHED
}
private:
friend std::ostream& operator<<(std::ostream&, const Value&);
int type;
peglib::any v;
};
std::ostream& operator<<(std::ostream& os, const Value& val);
struct Env
{
Env() : outer_(nullptr) {}
Env(Env& outer) : outer_(&outer) {}
bool has(const std::string& s) const {
if (dic_.find(s) != dic_.end()) {
return true;
}
if (outer_) {
return outer_->has(s);
}
return false;
}
Value get(const std::string& s) const {
assert(has(s));
if (dic_.find(s) != dic_.end()) {
return dic_.at(s);
}
return outer_->get(s);
}
void set(const std::string& s, const Value& val) {
dic_[s] = val;
}
void setup_built_in_functions() {
auto func_print = Value::FunctionValue {
{ "arg" },
[](Env& env) {
std::cout << env.get("arg") << std::endl;
return Value();
}
};
set("print", Value(func_print));
}
private:
Env* outer_;
std::map<std::string, Value> dic_;
};
bool run(Env& env, const char* expr, size_t len, Value& val, std::string& msg, bool print_ast);

File diff suppressed because it is too large Load Diff

@ -0,0 +1,73 @@
#include "repl.hpp"
#include <fstream>
#include <iostream>
#include <vector>
using namespace std;
bool read_file(const char* path, vector<char>& buff)
{
ifstream ifs(path, ios::in|ios::binary);
if (ifs.fail()) {
return false;
}
auto size = static_cast<unsigned int>(ifs.seekg(0, ios::end).tellg());
if (size > 0) {
buff.resize(size);
ifs.seekg(0, ios::beg).read(&buff[0], static_cast<streamsize>(buff.size()));
}
return true;
}
int main(int argc, const char** argv)
{
auto print_ast = false;
auto shell = false;
vector<const char*> path_list;
int argi = 1;
while (argi < argc) {
auto arg = argv[argi++];
if (string("--shell") == arg) {
shell = true;
} else if (string("--ast") == arg) {
print_ast = true;
} else {
path_list.push_back(arg);
}
}
if (!shell) {
shell = path_list.empty();
}
Env env;
env.setup_built_in_functions();
for (auto path: path_list) {
vector<char> buff;
if (!read_file(path, buff)) {
cerr << "can't open '" << path << "'." << endl;
return -1;
}
Value val;
string msg;
if (!run(env, buff.data(), buff.size(), val, msg, print_ast)) {
cerr << "error in '" << path << "'." << endl;
cerr << msg.c_str() << endl;
return -1;
}
}
if (shell) {
repl(env, print_ast);
}
return 0;
}
// vim: et ts=4 sw=4 cin cino={1s ff=unix

@ -0,0 +1,72 @@
#include "parser.hpp"
using namespace peglib;
using namespace std;
static auto g_grammar = R"(
PROGRAM <- _ STATEMENTS
STATEMENTS <- EXPRESSION*
EXPRESSION <- ASSIGNMENT / PRIMARY
ASSIGNMENT <- IDENTIFIER '=' _ EXPRESSION
WHILE <- 'while' _ EXPRESSION BLOCK
IF <- 'if' _ EXPRESSION BLOCK ('else' _ BLOCK)?
FUNCTION <- 'fn' _ PARAMETERS BLOCK
PARAMETERS <- '(' _ IDENTIFIER* ')' _
FUNCTION_CALL <- IDENTIFIER ARGUMENTS
ARGUMENTS <- '(' _ EXPRESSION* ')' _
PRIMARY <- CONDITION (CONDITION_OPERATOR CONDITION)?
CONDITION <- TERM (TERM_OPERATOR TERM)*
TERM <- FACTOR (FACTOR_OPERATOR FACTOR)*
FACTOR <- WHILE / IF / FUNCTION / FUNCTION_CALL / NUMBER / BOOLEAN / STRING / IDENTIFIER / '(' _ EXPRESSION ')' _
BLOCK <- '{' _ STATEMENTS '}' _
CONDITION_OPERATOR <- < ('==' / '!=' / '<=' / '<' / '>=' / '>') > _
TERM_OPERATOR <- < [-+] > _
FACTOR_OPERATOR <- < [*/] > _
IDENTIFIER <- < [a-zA-Z_]+ > _
NUMBER <- < [0-9]+ > _
BOOLEAN <- < ('true' / 'false') > _
STRING <- ['] < (!['] .)* > ['] _
~_ <- (Space / EndOfLine / Comment)*
Space <- ' ' / '\t'
EndOfLine <- '\r\n' / '\n' / '\r'
EndOfFile <- !.
Comment <- '/*' (!'*/' .)* '*/' / ('#' / '//') (!(EndOfLine / EndOfFile) .)* (EndOfLine / EndOfFile)
)";
static auto g_parser = peg(g_grammar)
.ast_node_optimizable("STATEMENTS", Statements)
.ast_node("WHILE", While)
.ast_node("ASSIGNMENT", Assignment)
.ast_node("IF", If)
.ast_node("FUNCTION", Function)
.ast_node("PARAMETERS")
.ast_node("FUNCTION_CALL", FunctionCall)
.ast_node("ARGUMENTS")
.ast_node_optimizable("PRIMARY", Condition)
.ast_node_optimizable("CONDITION", BinExpresion)
.ast_node_optimizable("TERM", BinExpresion)
.ast_token("CONDITION_OPERATOR")
.ast_token("TERM_OPERATOR")
.ast_token("FACTOR_OPERATOR")
.ast_token("NUMBER", Number)
.ast_token("BOOLEAN", Boolean)
.ast_token("STRING")
.ast_token("IDENTIFIER", Identifier)
.ast_end()
.set_logger([&](size_t ln, size_t col, const string& msg) {
cerr << ln << ":" << col << ": " << msg << endl;
});
const peglib::peg& get_parser()
{
return g_parser;
}
// vim: et ts=4 sw=4 cin cino={1s ff=unix

@ -0,0 +1,11 @@
#include <peglib.h>
enum AstType
{
Statements, While, If, FunctionCall, Assignment, Condition, BinExpresion,
Identifier, Number, Boolean, Function
};
const peglib::peg& get_parser();
// vim: et ts=4 sw=4 cin cino={1s ff=unix

@ -0,0 +1,31 @@
#include "linenoise.hpp"
#include "repl.hpp"
#include <iostream>
using namespace std;
int repl(Env& env, bool print_ast)
{
for (;;) {
auto line = linenoise::Readline("cul> ");
if (line == "exit" || line == "quit") {
break;
}
if (!line.empty()) {
Value val;
string msg;
if (run(env, line.c_str(), line.size(), val, msg, print_ast)) {
cout << val << endl;
linenoise::AddHistory(line.c_str());
} else if (!msg.empty()) {
cout << msg << endl;
}
}
}
return 0;
}
// vim: et ts=4 sw=4 cin cino={1s ff=unix

@ -0,0 +1,5 @@
#include "interpreter.hpp"
int repl(Env& env, bool print_ast);
// vim: et ts=4 sw=4 cin cino={1s ff=unix
Loading…
Cancel
Save