diff --git a/Makefile b/Makefile index 8e1418e..e6ea536 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,8 @@ parser.cpp parser.hpp: parser.y lexer.cpp: lexer.l parser.hpp flex -o lexer.cpp lexer.l -mycompiler: parser.cpp lexer.cpp main.cpp - $(CXX) $(CXXFLAGS) -o $@ parser.cpp lexer.cpp main.cpp -lfl +mycompiler: parser.cpp lexer.cpp main.cpp semantic.cpp interpreter.cpp + $(CXX) $(CXXFLAGS) -o $@ parser.cpp lexer.cpp main.cpp semantic.cpp interpreter.cpp -lfl clean: rm -f parser.cpp parser.hpp lexer.cpp mycompiler diff --git a/ast.hpp b/ast.hpp index dba43ba..76c9029 100644 --- a/ast.hpp +++ b/ast.hpp @@ -6,157 +6,149 @@ namespace AST { -struct Node { - virtual ~Node() = default; - virtual void print(std::ostream& os, int indent = 0) const = 0; -}; +struct Node { virtual ~Node() = default; virtual void print(std::ostream& os, int indent = 0) const = 0; }; -inline void doIndent(std::ostream& os, int n) { - for (int i = 0; i < n; ++i) os << " "; -} +inline void doIndent(std::ostream& os, int n) { for (int i = 0; i < n; ++i) os << " "; } struct Expr : Node { }; struct IntLiteral : Expr { std::int64_t value; explicit IntLiteral(std::int64_t v) : value(v) {} - void print(std::ostream& os, int indent) const override { - doIndent(os, indent); os << "Int(" << value << ")\n"; - } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "Int(" << value << ")\n"; } +}; + +struct StringLiteral : Expr { + std::string value; + explicit StringLiteral(std::string v) : value(std::move(v)) {} + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "String(" << value << ")\n"; } }; struct BoolLiteral : Expr { bool value; explicit BoolLiteral(bool v) : value(v) {} - void print(std::ostream& os, int indent) const override { - doIndent(os, indent); os << "Bool(" << (value ? "true" : "false") << ")\n"; - } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "Bool(" << (value ? "true" : "false") << ")\n"; } }; struct Identifier : Expr { std::string name; explicit Identifier(std::string n) : name(std::move(n)) {} - void print(std::ostream& os, int indent) const override { - doIndent(os, indent); os << "Id(" << name << ")\n"; - } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "Id(" << name << ")\n"; } }; -enum class BinOp { Add, Sub, Mul, Div, Assign }; +enum class BinOp { Add, Sub, Mul, Div, Assign, Gt, Lt, Eq }; struct Binary : Expr { - BinOp op; - Expr* lhs; - Expr* rhs; + BinOp op; Expr* lhs; Expr* rhs; Binary(BinOp o, Expr* l, Expr* r) : op(o), lhs(l), rhs(r) {} ~Binary() { delete lhs; delete rhs; } static const char* opToStr(BinOp o) { - switch (o) { - case BinOp::Add: return "+"; - case BinOp::Sub: return "-"; - case BinOp::Mul: return "*"; - case BinOp::Div: return "/"; - case BinOp::Assign: return "="; - } + switch (o) { case BinOp::Add: return "+"; case BinOp::Sub: return "-"; case BinOp::Mul: return "*"; case BinOp::Div: return "/"; case BinOp::Assign: return ":="; case BinOp::Gt: return ">"; case BinOp::Lt: return "<"; case BinOp::Eq: return "=="; } return "?"; } - void print(std::ostream& os, int indent) const override { - doIndent(os, indent); - os << "BinOp(" << opToStr(op) << ")\n"; - lhs->print(os, indent + 1); - rhs->print(os, indent + 1); - } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "BinOp(" << opToStr(op) << ")\n"; lhs->print(os, indent + 1); rhs->print(os, indent + 1); } }; struct Unary : Expr { - enum class Op { Neg }; - Op op; - Expr* rhs; + enum class Op { Neg }; Op op; Expr* rhs; Unary(Op o, Expr* e) : op(o), rhs(e) {} ~Unary() { delete rhs; } - void print(std::ostream& os, int indent) const override { - doIndent(os, indent); - os << "Unary(-)\n"; - rhs->print(os, indent + 1); - } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "Unary(-)\n"; rhs->print(os, indent + 1); } +}; + +struct MemberAccess : Expr { + Expr* object; std::string member; + MemberAccess(Expr* o, std::string m) : object(o), member(std::move(m)) {} + ~MemberAccess() { delete object; } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "Member\n"; object->print(os, indent + 1); doIndent(os, indent + 1); os << member << "\n"; } +}; + +struct Index : Expr { + Expr* base; Expr* index; + Index(Expr* b, Expr* i) : base(b), index(i) {} + ~Index() { delete base; delete index; } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "Index\n"; base->print(os, indent + 1); index->print(os, indent + 1); } +}; + +struct Call : Expr { + Expr* callee; std::vector args; + explicit Call(Expr* c) : callee(c) {} + ~Call() { delete callee; for (auto* a : args) delete a; } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "Call\n"; callee->print(os, indent + 1); for (auto* a : args) a->print(os, indent + 1); } }; struct Stmt : Node { }; +struct ExprStmt : Stmt { + Expr* expr; + explicit ExprStmt(Expr* e) : expr(e) {} + ~ExprStmt() { delete expr; } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "expr\n"; expr->print(os, indent + 1); } +}; + struct ReturnStmt : Stmt { Expr* value; explicit ReturnStmt(Expr* v) : value(v) {} ~ReturnStmt() { delete value; } - void print(std::ostream& os, int indent) const override { - doIndent(os, indent); os << "return\n"; - value->print(os, indent + 1); - } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "return\n"; value->print(os, indent + 1); } }; struct IfStmt : Stmt { - Expr* cond; - Stmt* thenS; - Stmt* elseS; + Expr* cond; Stmt* thenS; Stmt* elseS; IfStmt(Expr* c, Stmt* t, Stmt* e) : cond(c), thenS(t), elseS(e) {} ~IfStmt() { delete cond; delete thenS; delete elseS; } - void print(std::ostream& os, int indent) const override { - doIndent(os, indent); os << "if\n"; - cond->print(os, indent + 1); - doIndent(os, indent); os << "then\n"; - thenS->print(os, indent + 1); - doIndent(os, indent); os << "else\n"; - elseS->print(os, indent + 1); - } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "if\n"; cond->print(os, indent + 1); doIndent(os, indent); os << "then\n"; thenS->print(os, indent + 1); doIndent(os, indent); os << "else\n"; elseS->print(os, indent + 1); } +}; + +struct WhileStmt : Stmt { + Expr* cond; Stmt* body; + WhileStmt(Expr* c, Stmt* b) : cond(c), body(b) {} + ~WhileStmt() { delete cond; delete body; } + void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "while\n"; cond->print(os, indent + 1); body->print(os, indent + 1); } +}; + +struct Block : Stmt { + std::vector stmts; + ~Block() { for (auto* s : stmts) delete s; } + void print(std::ostream& os, int indent) const override { for (auto* s : stmts) s->print(os, indent); } }; struct VarDecl : Node { - std::string name; - std::string typeName; - Expr* init; - VarDecl(std::string n, std::string t, Expr* i) - : name(std::move(n)), typeName(std::move(t)), init(i) {} + std::string name; std::string typeName; Expr* init; + VarDecl(std::string n, std::string t, Expr* i) : name(std::move(n)), typeName(std::move(t)), init(i) {} ~VarDecl() { delete init; } void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "var " << name << " : " << typeName; - if (init) { - os << " =\n"; - init->print(os, indent + 1); - } else { - os << "\n"; - } + if (init) { os << " =\n"; init->print(os, indent + 1); } + else { os << "\n"; } } }; -struct Param { - std::string name; - std::string typeName; - Param(std::string n, std::string t) : name(std::move(n)), typeName(std::move(t)) {} +struct VarDeclStmt : Stmt { + VarDecl* decl; + explicit VarDeclStmt(VarDecl* d) : decl(d) {} + ~VarDeclStmt() { delete decl; } + void print(std::ostream& os, int indent) const override { decl->print(os, indent); } }; +struct Param { std::string name; std::string typeName; Param(std::string n, std::string t) : name(std::move(n)), typeName(std::move(t)) {} }; + struct MethodDecl : Node { - std::string name; - std::vector params; - std::string returnType; - Stmt* body; - MethodDecl(std::string n, std::string rt, Stmt* b) - : name(std::move(n)), returnType(std::move(rt)), body(b) {} + std::string name; std::vector params; std::string returnType; Stmt* body; + MethodDecl(std::string n, std::string rt, Stmt* b) : name(std::move(n)), returnType(std::move(rt)), body(b) {} ~MethodDecl() { for (auto* p : params) delete p; delete body; } void print(std::ostream& os, int indent) const override { doIndent(os, indent); os << "method " << name << "("; - for (size_t i = 0; i < params.size(); ++i) { - os << params[i]->name << " : " << params[i]->typeName; - if (i + 1 < params.size()) os << ", "; - } + for (size_t i = 0; i < params.size(); ++i) { os << params[i]->name << " : " << params[i]->typeName; if (i + 1 < params.size()) os << ", "; } os << ") : " << returnType << "\n"; if (body) body->print(os, indent + 1); } }; struct ClassDecl : Node { - std::string name; - std::vector fields; - std::vector methods; + std::string name; std::vector fields; std::vector methods; explicit ClassDecl(std::string n) : name(std::move(n)) {} ~ClassDecl() { for (auto* v : fields) delete v; for (auto* m : methods) delete m; } void print(std::ostream& os, int indent) const override { @@ -170,9 +162,7 @@ struct ClassDecl : Node { struct Program : Node { std::vector classes; ~Program() { for (auto* c : classes) delete c; } - void print(std::ostream& os, int indent = 0) const override { - for (auto* c : classes) c->print(os, indent); - } + void print(std::ostream& os, int indent = 0) const override { for (auto* c : classes) c->print(os, indent); } }; } diff --git a/interpreter.cpp b/interpreter.cpp new file mode 100644 index 0000000..a814245 --- /dev/null +++ b/interpreter.cpp @@ -0,0 +1,249 @@ +#include "interpreter.hpp" +#include + +Interpreter::Interpreter(AST::Program* p) : program(p), mainInstance(nullptr) { + indexClasses(); +} + +void Interpreter::indexClasses() { + classes.clear(); + for (auto* c : program->classes) { + classes[c->name] = c; + } +} + +Value Interpreter::defaultValueForType(const std::string& typeName) { + if (typeName == "Int") return Value::makeInt(0); + if (typeName == "Bool") return Value::makeBool(false); + if (typeName == "String") return Value::makeString(""); + return Value::makeVoid(); +} + +AST::MethodDecl* Interpreter::findMethod(AST::ClassDecl* c, const std::string& name) { + for (auto* m : c->methods) { + if (m->name == name) return m; + } + return nullptr; +} + +Value Interpreter::evalExpr(AST::Expr* e, Frame& frame) { + if (!e) return Value::makeVoid(); + + if (auto* il = dynamic_cast(e)) { + return Value::makeInt(il->value); + } + if (auto* sl = dynamic_cast(e)) { + return Value::makeString(sl->value); + } + if (auto* bl = dynamic_cast(e)) { + return Value::makeBool(bl->value); + } + if (auto* id = dynamic_cast(e)) { + auto it = frame.locals.find(id->name); + if (it != frame.locals.end()) return it->second; + if (frame.self) { + auto fit = frame.self->fields.find(id->name); + if (fit != frame.self->fields.end()) return fit->second; + } + return Value::makeVoid(); + } + if (auto* bin = dynamic_cast(e)) { + if (bin->op == AST::BinOp::Assign) { + Value rv = evalExpr(bin->rhs, frame); + if (auto* lid = dynamic_cast(bin->lhs)) { + auto it = frame.locals.find(lid->name); + if (it != frame.locals.end()) { + it->second = rv; + return rv; + } + if (frame.self) { + auto fit = frame.self->fields.find(lid->name); + if (fit != frame.self->fields.end()) { + fit->second = rv; + return rv; + } + frame.self->fields[lid->name] = rv; + return rv; + } + frame.locals[lid->name] = rv; + return rv; + } + return rv; + } + Value lv = evalExpr(bin->lhs, frame); + Value rv = evalExpr(bin->rhs, frame); + if (bin->op == AST::BinOp::Add && lv.type == Value::Type::Int && rv.type == Value::Type::Int) { + return Value::makeInt(lv.i + rv.i); + } + if (bin->op == AST::BinOp::Sub && lv.type == Value::Type::Int && rv.type == Value::Type::Int) { + return Value::makeInt(lv.i - rv.i); + } + if (bin->op == AST::BinOp::Mul && lv.type == Value::Type::Int && rv.type == Value::Type::Int) { + return Value::makeInt(lv.i * rv.i); + } + if (bin->op == AST::BinOp::Div && lv.type == Value::Type::Int && rv.type == Value::Type::Int) { + if (rv.i == 0) return Value::makeInt(0); + return Value::makeInt(lv.i / rv.i); + } + if (bin->op == AST::BinOp::Gt && lv.type == Value::Type::Int && rv.type == Value::Type::Int) { + return Value::makeBool(lv.i > rv.i); + } + if (bin->op == AST::BinOp::Lt && lv.type == Value::Type::Int && rv.type == Value::Type::Int) { + return Value::makeBool(lv.i < rv.i); + } + if (bin->op == AST::BinOp::Eq) { + if (lv.type == rv.type) { + if (lv.type == Value::Type::Int) return Value::makeBool(lv.i == rv.i); + if (lv.type == Value::Type::Bool) return Value::makeBool(lv.b == rv.b); + if (lv.type == Value::Type::String) return Value::makeBool(lv.s == rv.s); + } + return Value::makeBool(false); + } + return Value::makeVoid(); + } + if (auto* un = dynamic_cast(e)) { + Value rv = evalExpr(un->rhs, frame); + if (un->op == AST::Unary::Op::Neg && rv.type == Value::Type::Int) { + return Value::makeInt(-rv.i); + } + return Value::makeVoid(); + } + if (auto* call = dynamic_cast(e)) { + if (auto* id = dynamic_cast(call->callee)) { + if (id->name == "output" || id->name == "print") { + std::vector args; + for (auto* a : call->args) args.push_back(evalExpr(a, frame)); + for (size_t i = 0; i < args.size(); ++i) { + if (i > 0) std::cout << " "; + std::cout << args[i].toString(); + } + std::cout << std::endl; + return Value::makeVoid(); + } + if (frame.self && frame.self->cls) { + AST::MethodDecl* m = findMethod(frame.self->cls, id->name); + if (m) { + std::vector args; + for (auto* a : call->args) args.push_back(evalExpr(a, frame)); + return callMethod(frame.self, m, args); + } + } + return Value::makeVoid(); + } + return Value::makeVoid(); + } + if (auto* ma = dynamic_cast(e)) { + Value obj = evalExpr(ma->object, frame); + return Value::makeVoid(); + } + if (auto* idx = dynamic_cast(e)) { + Value base = evalExpr(idx->base, frame); + Value ind = evalExpr(idx->index, frame); + return Value::makeVoid(); + } + return Value::makeVoid(); +} + +void Interpreter::execBlock(AST::Block* b, Frame& frame, bool& returned, Value& retVal) { + if (!b) return; + for (auto* s : b->stmts) { + if (returned) return; + execStmt(s, frame, returned, retVal); + } +} + +void Interpreter::execStmt(AST::Stmt* s, Frame& frame, bool& returned, Value& retVal) { + if (!s || returned) return; + + if (auto* ifs = dynamic_cast(s)) { + Value cv = evalExpr(ifs->cond, frame); + bool cond = false; + if (cv.type == Value::Type::Bool) cond = cv.b; + if (cond) { + execStmt(ifs->thenS, frame, returned, retVal); + } else { + execStmt(ifs->elseS, frame, returned, retVal); + } + return; + } + if (auto* w = dynamic_cast(s)) { + while (!returned) { + Value cv = evalExpr(w->cond, frame); + bool cond = false; + if (cv.type == Value::Type::Bool) cond = cv.b; + if (!cond) break; + if (auto* blk = dynamic_cast(w->body)) { + execBlock(blk, frame, returned, retVal); + } else { + execStmt(w->body, frame, returned, retVal); + } + } + return; + } + if (auto* ret = dynamic_cast(s)) { + retVal = evalExpr(ret->value, frame); + returned = true; + return; + } + if (auto* es = dynamic_cast(s)) { + evalExpr(es->expr, frame); + return; + } + if (auto* vds = dynamic_cast(s)) { + if (vds->decl) { + Value v; + if (vds->decl->init) v = evalExpr(vds->decl->init, frame); + else v = defaultValueForType(vds->decl->typeName); + frame.locals[vds->decl->name] = v; + } + return; + } + if (auto* blk = dynamic_cast(s)) { + execBlock(blk, frame, returned, retVal); + return; + } +} + +Value Interpreter::callMethod(Instance* inst, AST::MethodDecl* m, const std::vector& args) { + Frame f; + f.self = inst; + for (size_t i = 0; i < m->params.size() && i < args.size(); ++i) { + f.locals[m->params[i]->name] = args[i]; + } + bool returned = false; + Value ret = Value::makeVoid(); + if (auto* blk = dynamic_cast(m->body)) { + execBlock(blk, f, returned, ret); + } else { + execStmt(m->body, f, returned, ret); + } + return ret; +} + +void Interpreter::run() { + auto it = classes.find("Main"); + if (it == classes.end()) return; + AST::ClassDecl* mainClass = it->second; + mainInstance = new Instance(mainClass); + for (auto* f : mainClass->fields) { + mainInstance->fields[f->name] = defaultValueForType(f->typeName); + } + Frame fieldFrame; + fieldFrame.self = mainInstance; + for (auto* f : mainClass->fields) { + if (f->init) { + Value v = evalExpr(f->init, fieldFrame); + mainInstance->fields[f->name] = v; + } + } + AST::MethodDecl* mainMethod = findMethod(mainClass, "main"); + if (!mainMethod) { + delete mainInstance; + mainInstance = nullptr; + return; + } + std::vector args; + callMethod(mainInstance, mainMethod, args); + delete mainInstance; + mainInstance = nullptr; +} diff --git a/interpreter.hpp b/interpreter.hpp new file mode 100644 index 0000000..1ff97e5 --- /dev/null +++ b/interpreter.hpp @@ -0,0 +1,79 @@ +#pragma once +#include "ast.hpp" +#include +#include +#include +#include + +struct Value { + enum class Type { Int, Bool, String, Void }; + Type type; + std::int64_t i; + bool b; + std::string s; + + Value() : type(Type::Void), i(0), b(false) {} + + static Value makeInt(std::int64_t v) { + Value r; + r.type = Type::Int; + r.i = v; + return r; + } + + static Value makeBool(bool v) { + Value r; + r.type = Type::Bool; + r.b = v; + return r; + } + + static Value makeString(const std::string& v) { + Value r; + r.type = Type::String; + r.s = v; + return r; + } + + static Value makeVoid() { + return Value(); + } + + std::string toString() const { + if (type == Type::Int) return std::to_string(i); + if (type == Type::Bool) return b ? "true" : "false"; + if (type == Type::String) return s; + return ""; + } +}; + +class Interpreter { +public: + explicit Interpreter(AST::Program* p); + void run(); + +private: + struct Instance { + AST::ClassDecl* cls; + std::unordered_map fields; + explicit Instance(AST::ClassDecl* c) : cls(c) {} + }; + + struct Frame { + Instance* self; + std::unordered_map locals; + Frame() : self(nullptr) {} + }; + + AST::Program* program; + std::unordered_map classes; + Instance* mainInstance; + + void indexClasses(); + Value evalExpr(AST::Expr* e, Frame& frame); + void execStmt(AST::Stmt* s, Frame& frame, bool& returned, Value& retVal); + void execBlock(AST::Block* b, Frame& frame, bool& returned, Value& retVal); + AST::MethodDecl* findMethod(AST::ClassDecl* c, const std::string& name); + Value callMethod(Instance* inst, AST::MethodDecl* m, const std::vector& args); + Value defaultValueForType(const std::string& typeName); +}; diff --git a/lexer.cpp b/lexer.cpp new file mode 100644 index 0000000..3000dd2 --- /dev/null +++ b/lexer.cpp @@ -0,0 +1,2118 @@ +#line 2 "lexer.cpp" + +#line 4 "lexer.cpp" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires + * access to the local variable yy_act. Since yyless() is a macro, it would break + * existing scanners that call yyless() from OUTSIDE yylex. + * One obvious solution it to make yy_act a global. I tried that, and saw + * a 5% performance hit in a non-yylineno scanner, because yy_act is + * normally declared as a register variable-- so it is not worth it. + */ + #define YY_LESS_LINENO(n) \ + do { \ + int yyl;\ + for ( yyl = n; yyl < yyleng; ++yyl )\ + if ( yytext[yyl] == '\n' )\ + --yylineno;\ + }while(0) + #define YY_LINENO_REWIND_TO(dst) \ + do {\ + const char *p;\ + for ( p = yy_cp-1; p >= (dst); --p)\ + if ( *p == '\n' )\ + --yylineno;\ + }while(0) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = NULL; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_flush_buffer ( YY_BUFFER_STATE b ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state ( void ); + +static void yyensure_buffer_stack ( void ); +static void yy_load_buffer_state ( void ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); + +void *yyalloc ( yy_size_t ); +void *yyrealloc ( void *, yy_size_t ); +void yyfree ( void * ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define yywrap() (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +FILE *yyin = NULL, *yyout = NULL; + +typedef int yy_state_type; + +extern int yylineno; +int yylineno = 1; + +extern char *yytext; +#ifdef yytext_ptr +#undef yytext_ptr +#endif +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yynoreturn yy_fatal_error ( const char* msg ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; +#define YY_NUM_RULES 47 +#define YY_END_OF_BUFFER 48 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[112] = + { 0, + 0, 0, 48, 46, 44, 45, 46, 29, 30, 38, + 36, 28, 37, 40, 39, 22, 26, 27, 42, 35, + 41, 21, 21, 21, 21, 21, 21, 33, 34, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 31, + 32, 44, 0, 20, 0, 43, 22, 23, 25, 24, + 21, 21, 21, 21, 21, 21, 21, 12, 21, 21, + 21, 8, 4, 21, 21, 21, 21, 21, 21, 43, + 21, 21, 15, 21, 21, 21, 21, 5, 21, 21, + 21, 21, 21, 3, 21, 17, 21, 21, 19, 21, + 10, 21, 21, 21, 9, 13, 21, 18, 21, 2, + + 0, 14, 21, 21, 11, 16, 0, 6, 7, 1, + 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 1, 1, 1, 1, 1, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 14, 15, 16, + 17, 18, 1, 1, 19, 20, 19, 19, 19, 21, + 19, 19, 22, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 23, 19, 19, 24, 19, 19, 19, 19, + 25, 26, 27, 1, 19, 1, 28, 19, 29, 30, + + 31, 32, 33, 34, 35, 19, 19, 36, 37, 38, + 39, 19, 19, 40, 41, 42, 43, 44, 45, 19, + 19, 19, 46, 1, 47, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[48] = + { 0, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 1, 1, 1, 1, 1, 3, 3, + 3, 3, 3, 3, 1, 1, 1, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 1, 1 + } ; + +static const flex_int16_t yy_base[115] = + { 0, + 0, 0, 128, 129, 125, 129, 44, 129, 129, 129, + 129, 129, 129, 129, 114, 112, 107, 129, 129, 32, + 129, 0, 84, 86, 83, 78, 80, 129, 129, 82, + 78, 15, 88, 20, 84, 83, 20, 85, 78, 129, + 129, 109, 51, 129, 107, 0, 96, 129, 129, 129, + 0, 69, 68, 64, 65, 69, 75, 0, 61, 71, + 64, 0, 0, 57, 56, 65, 47, 49, 53, 0, + 51, 58, 0, 50, 54, 42, 51, 0, 40, 46, + 36, 40, 45, 0, 39, 0, 32, 35, 0, 31, + 54, 40, 30, 28, 0, 0, 36, 0, 33, 0, + + 56, 0, 35, 26, 0, 0, 31, 0, 0, 129, + 129, 91, 59, 94 + } ; + +static const flex_int16_t yy_def[115] = + { 0, + 111, 1, 111, 111, 111, 111, 112, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 113, 113, 113, 113, 113, 113, 111, 111, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 113, 111, + 111, 111, 112, 111, 112, 114, 111, 111, 111, 111, + 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 113, 114, + 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, + + 111, 113, 113, 113, 113, 113, 111, 113, 113, 111, + 0, 111, 111, 111 + } ; + +static const flex_int16_t yy_nxt[177] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 4, 29, 22, 30, 31, + 32, 33, 22, 22, 34, 22, 35, 22, 22, 36, + 22, 37, 22, 38, 39, 40, 41, 44, 49, 50, + 59, 62, 60, 66, 44, 101, 101, 101, 101, 67, + 63, 51, 110, 109, 108, 106, 105, 104, 103, 45, + 102, 100, 99, 98, 97, 96, 45, 95, 94, 93, + 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, + 107, 43, 43, 43, 70, 82, 70, 81, 80, 79, + + 78, 77, 76, 75, 74, 73, 72, 71, 47, 111, + 42, 69, 68, 65, 64, 61, 58, 57, 56, 55, + 54, 53, 52, 48, 47, 46, 42, 111, 3, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111 + } ; + +static const flex_int16_t yy_chk[177] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 7, 20, 20, + 32, 34, 32, 37, 43, 91, 91, 101, 101, 37, + 34, 113, 107, 104, 103, 99, 97, 94, 93, 7, + 92, 90, 88, 87, 85, 83, 43, 82, 81, 80, + 79, 77, 76, 75, 74, 72, 71, 69, 68, 67, + 101, 112, 112, 112, 114, 66, 114, 65, 64, 61, + + 60, 59, 57, 56, 55, 54, 53, 52, 47, 45, + 42, 39, 38, 36, 35, 33, 31, 30, 27, 26, + 25, 24, 23, 17, 16, 15, 5, 3, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111 + } ; + +/* Table of booleans, true if rule could match eol. */ +static const flex_int32_t yy_rule_can_match_eol[48] = + { 0, +1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, }; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "lexer.l" +#line 2 "lexer.l" +#include +#include +#include +#include +#include +#include "parser.hpp" +#include "tokens.hpp" + +int yycolumn = 1; +extern int yylineno; + +static int expectClassName = 0; +static int expectMethodName = 0; +static int expectVarName = 0; +static int expectMemberName = 0; + +static inline void push_kw(TokenKind k, const char* text) { EmitToken(std::make_unique(k, text, yylineno, yycolumn)); yycolumn += yyleng; } +static inline void push_sym(TokenKind k, const char* text) { EmitToken(std::make_unique(k, text, yylineno, yycolumn)); yycolumn += yyleng; } +static inline int is_upper(const char* s) { return std::isupper((unsigned char)s[0]); } +#line 567 "lexer.cpp" +#line 568 "lexer.cpp" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals ( void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( void ); + +int yyget_debug ( void ); + +void yyset_debug ( int debug_flag ); + +YY_EXTRA_TYPE yyget_extra ( void ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in ( void ); + +void yyset_in ( FILE * _in_str ); + +FILE *yyget_out ( void ); + +void yyset_out ( FILE * _out_str ); + + int yyget_leng ( void ); + +char *yyget_text ( void ); + +int yyget_lineno ( void ); + +void yyset_lineno ( int _line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( void ); +#else +extern int yywrap ( void ); +#endif +#endif + +#ifndef YY_NO_UNPUT + + static void yyunput ( int c, char *buf_ptr ); + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * ); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + { +#line 33 "lexer.l" + + +#line 788 "lexer.cpp" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 112 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 129 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) + { + int yyl; + for ( yyl = 0; yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + + yylineno++; +; + } + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 35 "lexer.l" +{ push_kw(TokenKind::ELSEIF, yytext); return ELSEIF; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 36 "lexer.l" +{ push_kw(TokenKind::CLASS, yytext); expectClassName = 1; return CLASS; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 37 "lexer.l" +{ push_kw(TokenKind::VAR, yytext); expectVarName = 1; return VAR; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 38 "lexer.l" +{ push_kw(TokenKind::IS, yytext); return IS; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 39 "lexer.l" +{ push_kw(TokenKind::END, yytext); return END; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 40 "lexer.l" +{ push_kw(TokenKind::METHOD, yytext); expectMethodName = 1; return METHOD; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 41 "lexer.l" +{ push_kw(TokenKind::RETURN, yytext); return RETURN; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 42 "lexer.l" +{ push_kw(TokenKind::IF, yytext); return IF; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 43 "lexer.l" +{ push_kw(TokenKind::THEN, yytext); return THEN; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 44 "lexer.l" +{ push_kw(TokenKind::ELSE, yytext); return ELSE; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 45 "lexer.l" +{ push_kw(TokenKind::WHILE, yytext); return WHILE; } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 46 "lexer.l" +{ push_kw(TokenKind::DO, yytext); return DO; } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 47 "lexer.l" +{ push_kw(TokenKind::TRUEKW, yytext); return TRUE; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 48 "lexer.l" +{ push_kw(TokenKind::FALSEKW, yytext); return FALSE; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 50 "lexer.l" +{ EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 51 "lexer.l" +{ EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 52 "lexer.l" +{ EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 53 "lexer.l" +{ EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 54 "lexer.l" +{ EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } + YY_BREAK +case 20: +/* rule 20 can match eol */ +YY_RULE_SETUP +#line 56 "lexer.l" +{ std::string s(yytext); EmitToken(std::make_unique(yytext, s, yylineno, yycolumn)); yylval.cstr = strdup(s.c_str()); yycolumn += yyleng; return STRING_LITERAL; } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 58 "lexer.l" +{ + if (expectClassName) { expectClassName = 0; EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; } + if (expectMethodName) { expectMethodName = 0; EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; } + if (expectVarName) { expectVarName = 0; EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; } + if (expectMemberName) { expectMemberName = 0; EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; } + if (is_upper(yytext)) { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } + EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; + } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 67 "lexer.l" +{ long long v = atoll(yytext); EmitToken(std::make_unique(yytext, v, yylineno, yycolumn)); yylval.ival = v; yycolumn += yyleng; return INT_LITERAL; } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 69 "lexer.l" +{ push_sym(TokenKind::ASSIGN, yytext); return ASSIGN; } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 70 "lexer.l" +{ push_sym(TokenKind::ARROW, yytext); return ARROW; } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 71 "lexer.l" +{ push_sym(TokenKind::EQEQ, yytext); return EQEQ; } + YY_BREAK +case 26: +YY_RULE_SETUP +#line 72 "lexer.l" +{ push_sym(TokenKind::COLON, yytext); return COLON; } + YY_BREAK +case 27: +YY_RULE_SETUP +#line 73 "lexer.l" +{ push_sym(TokenKind::SEMICOLON, yytext); return SEMICOLON; } + YY_BREAK +case 28: +YY_RULE_SETUP +#line 74 "lexer.l" +{ push_sym(TokenKind::COMMA, yytext); return COMMA; } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 75 "lexer.l" +{ push_sym(TokenKind::LPAREN, yytext); return LPAREN; } + YY_BREAK +case 30: +YY_RULE_SETUP +#line 76 "lexer.l" +{ push_sym(TokenKind::RPAREN, yytext); return RPAREN; } + YY_BREAK +case 31: +YY_RULE_SETUP +#line 77 "lexer.l" +{ push_sym(TokenKind::LBRACE, yytext); return LBRACE; } + YY_BREAK +case 32: +YY_RULE_SETUP +#line 78 "lexer.l" +{ push_sym(TokenKind::RBRACE, yytext); return RBRACE; } + YY_BREAK +case 33: +YY_RULE_SETUP +#line 79 "lexer.l" +{ push_sym(TokenKind::LBRACKET, yytext); return LBRACKET; } + YY_BREAK +case 34: +YY_RULE_SETUP +#line 80 "lexer.l" +{ push_sym(TokenKind::RBRACKET, yytext); return RBRACKET; } + YY_BREAK +case 35: +YY_RULE_SETUP +#line 81 "lexer.l" +{ push_sym(TokenKind::EQUAL, yytext); return EQUAL; } + YY_BREAK +case 36: +YY_RULE_SETUP +#line 82 "lexer.l" +{ push_sym(TokenKind::PLUS, yytext); return PLUS; } + YY_BREAK +case 37: +YY_RULE_SETUP +#line 83 "lexer.l" +{ push_sym(TokenKind::MINUS, yytext); return MINUS; } + YY_BREAK +case 38: +YY_RULE_SETUP +#line 84 "lexer.l" +{ push_sym(TokenKind::STAR, yytext); return STAR; } + YY_BREAK +case 39: +YY_RULE_SETUP +#line 85 "lexer.l" +{ push_sym(TokenKind::SLASH, yytext); return SLASH; } + YY_BREAK +case 40: +YY_RULE_SETUP +#line 86 "lexer.l" +{ push_sym(TokenKind::DOT, yytext); expectMemberName = 1; return DOT; } + YY_BREAK +case 41: +YY_RULE_SETUP +#line 87 "lexer.l" +{ push_sym(TokenKind::GT, yytext); return GT; } + YY_BREAK +case 42: +YY_RULE_SETUP +#line 88 "lexer.l" +{ push_sym(TokenKind::LT, yytext); return LT; } + YY_BREAK +case 43: +YY_RULE_SETUP +#line 90 "lexer.l" +{ yycolumn += yyleng; } + YY_BREAK +case 44: +YY_RULE_SETUP +#line 91 "lexer.l" +{ yycolumn += yyleng; } + YY_BREAK +case 45: +/* rule 45 can match eol */ +YY_RULE_SETUP +#line 92 "lexer.l" +{ yycolumn = 1; } + YY_BREAK +case 46: +YY_RULE_SETUP +#line 94 "lexer.l" +{ std::fprintf(stderr, "Unknown char '%s' at %d:%d\n", yytext, yylineno, yycolumn); yycolumn += yyleng; } + YY_BREAK +case 47: +YY_RULE_SETUP +#line 96 "lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1100 "lexer.cpp" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = (yytext_ptr); + int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + yy_state_type yy_current_state; + char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 112 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + int yy_is_jam; + char *yy_cp = (yy_c_buf_p); + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 112 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 111); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + + static void yyunput (int c, char * yy_bp ) +{ + char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up yytext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + int number_to_move = (yy_n_chars) + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + if ( c == '\n' ){ + --yylineno; + } + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return 0; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + if ( c == '\n' ) + + yylineno++; +; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf ); + + yyfree( (void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + yy_size_t num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr ) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg ) +{ + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param _line_number line number + * + */ +void yyset_lineno (int _line_number ) +{ + + yylineno = _line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str ) +{ + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str ) +{ + yyout = _out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int _bdebug ) +{ + yy_flex_debug = _bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + /* We do not touch yylineno unless the option is enabled. */ + yylineno = 1; + + (yy_buffer_stack) = NULL; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = NULL; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n ) +{ + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s ) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 96 "lexer.l" + + diff --git a/lexer.l b/lexer.l index 4655ae5..7d62480 100644 --- a/lexer.l +++ b/lexer.l @@ -3,20 +3,21 @@ #include #include #include +#include #include "parser.hpp" #include "tokens.hpp" int yycolumn = 1; extern int yylineno; -static inline void push_kw(TokenKind k, const char* text) { - EmitToken(std::make_unique(k, text, yylineno, yycolumn)); - yycolumn += yyleng; -} -static inline void push_sym(TokenKind k, const char* text) { - EmitToken(std::make_unique(k, text, yylineno, yycolumn)); - yycolumn += yyleng; -} +static int expectClassName = 0; +static int expectMethodName = 0; +static int expectVarName = 0; +static int expectMemberName = 0; + +static inline void push_kw(TokenKind k, const char* text) { EmitToken(std::make_unique(k, text, yylineno, yycolumn)); yycolumn += yyleng; } +static inline void push_sym(TokenKind k, const char* text) { EmitToken(std::make_unique(k, text, yylineno, yycolumn)); yycolumn += yyleng; } +static inline int is_upper(const char* s) { return std::isupper((unsigned char)s[0]); } %} %option noyywrap @@ -24,41 +25,50 @@ static inline void push_sym(TokenKind k, const char* text) { %option yylineno ID [A-Za-z_][A-Za-z0-9_]* -INT [0-9]+ +NUM [0-9]+ +ESC \\. +STR \"([^\\\"]|{ESC})*\" WS [ \t\r]+ %% -"class" { push_kw(TokenKind::CLASS, yytext); return CLASS; } -"var" { push_kw(TokenKind::VAR, yytext); return VAR; } +"else"[ \t\r\n]+"if" { push_kw(TokenKind::ELSEIF, yytext); return ELSEIF; } +"class" { push_kw(TokenKind::CLASS, yytext); expectClassName = 1; return CLASS; } +"var" { push_kw(TokenKind::VAR, yytext); expectVarName = 1; return VAR; } "is" { push_kw(TokenKind::IS, yytext); return IS; } "end" { push_kw(TokenKind::END, yytext); return END; } - -"method" { push_kw(TokenKind::METHOD, yytext); return METHOD; } +"method" { push_kw(TokenKind::METHOD, yytext); expectMethodName = 1; return METHOD; } "return" { push_kw(TokenKind::RETURN, yytext); return RETURN; } "if" { push_kw(TokenKind::IF, yytext); return IF; } "then" { push_kw(TokenKind::THEN, yytext); return THEN; } "else" { push_kw(TokenKind::ELSE, yytext); return ELSE; } +"while" { push_kw(TokenKind::WHILE, yytext); return WHILE; } +"do" { push_kw(TokenKind::DO, yytext); return DO; } "true" { push_kw(TokenKind::TRUEKW, yytext); return TRUE; } "false" { push_kw(TokenKind::FALSEKW, yytext); return FALSE; } -"Int" { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); - yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } -"String" { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); - yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } -"Bool" { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); - yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } -"Float" { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); - yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } +"Int" { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } +"String" { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } +"Bool" { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } +"Float" { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } +"Void" { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } + +{STR} { std::string s(yytext); EmitToken(std::make_unique(yytext, s, yylineno, yycolumn)); yylval.cstr = strdup(s.c_str()); yycolumn += yyleng; return STRING_LITERAL; } -{ID} { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); - yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; } +{ID} { + if (expectClassName) { expectClassName = 0; EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; } + if (expectMethodName) { expectMethodName = 0; EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; } + if (expectVarName) { expectVarName = 0; EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; } + if (expectMemberName) { expectMemberName = 0; EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; } + if (is_upper(yytext)) { EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return TYPE_NAME; } + EmitToken(std::make_unique(yytext, yylineno, yycolumn)); yylval.cstr = strdup(yytext); yycolumn += yyleng; return IDENTIFIER; + } -{INT} { long long v = atoll(yytext); - EmitToken(std::make_unique(yytext, v, yylineno, yycolumn)); - yylval.ival = v; yycolumn += yyleng; return INT_LITERAL; } +{NUM} { long long v = atoll(yytext); EmitToken(std::make_unique(yytext, v, yylineno, yycolumn)); yylval.ival = v; yycolumn += yyleng; return INT_LITERAL; } +":=" { push_sym(TokenKind::ASSIGN, yytext); return ASSIGN; } "=>" { push_sym(TokenKind::ARROW, yytext); return ARROW; } +"==" { push_sym(TokenKind::EQEQ, yytext); return EQEQ; } ":" { push_sym(TokenKind::COLON, yytext); return COLON; } ";" { push_sym(TokenKind::SEMICOLON, yytext); return SEMICOLON; } "," { push_sym(TokenKind::COMMA, yytext); return COMMA; } @@ -66,17 +76,21 @@ WS [ \t\r]+ ")" { push_sym(TokenKind::RPAREN, yytext); return RPAREN; } "{" { push_sym(TokenKind::LBRACE, yytext); return LBRACE; } "}" { push_sym(TokenKind::RBRACE, yytext); return RBRACE; } -"=" { push_sym(TokenKind::ASSIGN, yytext); return ASSIGN; } +"[" { push_sym(TokenKind::LBRACKET, yytext); return LBRACKET; } +"]" { push_sym(TokenKind::RBRACKET, yytext); return RBRACKET; } +"=" { push_sym(TokenKind::EQUAL, yytext); return EQUAL; } "+" { push_sym(TokenKind::PLUS, yytext); return PLUS; } "-" { push_sym(TokenKind::MINUS, yytext); return MINUS; } "*" { push_sym(TokenKind::STAR, yytext); return STAR; } "/" { push_sym(TokenKind::SLASH, yytext); return SLASH; } +"." { push_sym(TokenKind::DOT, yytext); expectMemberName = 1; return DOT; } +">" { push_sym(TokenKind::GT, yytext); return GT; } +"<" { push_sym(TokenKind::LT, yytext); return LT; } "//".* { yycolumn += yyleng; } {WS} { yycolumn += yyleng; } \n { yycolumn = 1; } -. { std::fprintf(stderr, "Unknown char '%s' at %d:%d\n", yytext, yylineno, yycolumn); - yycolumn += yyleng; } +. { std::fprintf(stderr, "Unknown char '%s' at %d:%d\n", yytext, yylineno, yycolumn); yycolumn += yyleng; } %% diff --git a/main.cpp b/main.cpp index 0e358f5..94b00f9 100644 --- a/main.cpp +++ b/main.cpp @@ -2,8 +2,10 @@ #include #include "ast.hpp" #include "tokens.hpp" +#include "semantic.hpp" +#include "interpreter.hpp" -extern int yyparse(void); +extern int yyparse(void); extern FILE* yyin; extern AST::Program* g_program; @@ -12,24 +14,79 @@ int main(int argc, char** argv) { std::cerr << "Usage: " << argv[0] << " \n"; return 1; } + + FILE* src = std::fopen(argv[1], "r"); + if (!src) { + std::perror("fopen"); + return 1; + } + + std::cout << "=== SOURCE ===\n"; + int ch; + while ((ch = std::fgetc(src)) != EOF) { + std::cout << static_cast(ch); + } + std::cout << "\n"; + std::fclose(src); + yyin = std::fopen(argv[1], "r"); if (!yyin) { std::perror("fopen"); return 1; } + std::cout << "=== LEXER TOKENS ===\n"; int rc = yyparse(); std::fclose(yyin); - if (rc == 0) { - std::cout << "\n=== AST ===\n"; - if (g_program) { - g_program->print(std::cout); - delete g_program; - g_program = nullptr; - } - return 0; - } else { + + if (rc != 0) { std::cerr << "Parse failed.\n"; return 2; } + + if (!g_program) { + std::cerr << "No AST generated.\n"; + return 2; + } + + std::cout << "\n=== Semantic Analysis ===\n"; + SemanticAnalyzer analyzer; + SemanticResult res = analyzer.analyze(g_program); + + if (!res.ok) { + std::cerr << "Semantic errors found:\n"; + for (const auto& e : res.errors) { + std::cerr << " - " << e << "\n"; + } + delete g_program; + g_program = nullptr; + return 3; + } + + std::cout << "Semantic analysis completed successfully.\n"; + + if (!res.warnings.empty()) { + std::cout << "Warnings:\n"; + for (const auto& w : res.warnings) { + std::cout << " - " << w << "\n"; + } + } + + if (!res.optimizations.empty()) { + std::cout << "Optimizations applied:\n"; + for (const auto& o : res.optimizations) { + std::cout << " * " << o << "\n"; + } + } + + std::cout << "\n=== Final AST (After Semantic Analysis) ===\n"; + g_program->print(std::cout); + + std::cout << "\n=== Interpretation ===\n"; + Interpreter interp(g_program); + interp.run(); + + delete g_program; + g_program = nullptr; + return 0; } diff --git a/mycompiler b/mycompiler new file mode 100644 index 0000000..6a97839 Binary files /dev/null and b/mycompiler differ diff --git a/parser.cpp b/parser.cpp new file mode 100644 index 0000000..72197af --- /dev/null +++ b/parser.cpp @@ -0,0 +1,2274 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30802 + +/* Bison version string. */ +#define YYBISON_VERSION "3.8.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 1 "parser.y" + +#include +#include +#include +#include +#include +#include "ast.hpp" +#include "tokens.hpp" + +extern int yylex(void); +extern int yylineno; +void yyerror(const char* s); + +AST::Program* g_program = nullptr; + +#line 87 "parser.cpp" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#include "parser.hpp" +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_CLASS = 3, /* CLASS */ + YYSYMBOL_VAR = 4, /* VAR */ + YYSYMBOL_IS = 5, /* IS */ + YYSYMBOL_END = 6, /* END */ + YYSYMBOL_METHOD = 7, /* METHOD */ + YYSYMBOL_RETURN = 8, /* RETURN */ + YYSYMBOL_IF = 9, /* IF */ + YYSYMBOL_THEN = 10, /* THEN */ + YYSYMBOL_ELSE = 11, /* ELSE */ + YYSYMBOL_ELSEIF = 12, /* ELSEIF */ + YYSYMBOL_WHILE = 13, /* WHILE */ + YYSYMBOL_DO = 14, /* DO */ + YYSYMBOL_TRUE = 15, /* TRUE */ + YYSYMBOL_FALSE = 16, /* FALSE */ + YYSYMBOL_COLON = 17, /* COLON */ + YYSYMBOL_SEMICOLON = 18, /* SEMICOLON */ + YYSYMBOL_COMMA = 19, /* COMMA */ + YYSYMBOL_LPAREN = 20, /* LPAREN */ + YYSYMBOL_RPAREN = 21, /* RPAREN */ + YYSYMBOL_LBRACE = 22, /* LBRACE */ + YYSYMBOL_RBRACE = 23, /* RBRACE */ + YYSYMBOL_LBRACKET = 24, /* LBRACKET */ + YYSYMBOL_RBRACKET = 25, /* RBRACKET */ + YYSYMBOL_ASSIGN = 26, /* ASSIGN */ + YYSYMBOL_ARROW = 27, /* ARROW */ + YYSYMBOL_PLUS = 28, /* PLUS */ + YYSYMBOL_MINUS = 29, /* MINUS */ + YYSYMBOL_STAR = 30, /* STAR */ + YYSYMBOL_SLASH = 31, /* SLASH */ + YYSYMBOL_DOT = 32, /* DOT */ + YYSYMBOL_GT = 33, /* GT */ + YYSYMBOL_LT = 34, /* LT */ + YYSYMBOL_EQEQ = 35, /* EQEQ */ + YYSYMBOL_EQUAL = 36, /* EQUAL */ + YYSYMBOL_IDENTIFIER = 37, /* IDENTIFIER */ + YYSYMBOL_TYPE_NAME = 38, /* TYPE_NAME */ + YYSYMBOL_INT_LITERAL = 39, /* INT_LITERAL */ + YYSYMBOL_STRING_LITERAL = 40, /* STRING_LITERAL */ + YYSYMBOL_YYACCEPT = 41, /* $accept */ + YYSYMBOL_program = 42, /* program */ + YYSYMBOL_class_list = 43, /* class_list */ + YYSYMBOL_class_decl = 44, /* class_decl */ + YYSYMBOL_class_body = 45, /* class_body */ + YYSYMBOL_member_list = 46, /* member_list */ + YYSYMBOL_member = 47, /* member */ + YYSYMBOL_type_spec = 48, /* type_spec */ + YYSYMBOL_type_list = 49, /* type_list */ + YYSYMBOL_var_decl = 50, /* var_decl */ + YYSYMBOL_method_decl = 51, /* method_decl */ + YYSYMBOL_opt_params = 52, /* opt_params */ + YYSYMBOL_param_list = 53, /* param_list */ + YYSYMBOL_param = 54, /* param */ + YYSYMBOL_method_body = 55, /* method_body */ + YYSYMBOL_stmt_list = 56, /* stmt_list */ + YYSYMBOL_simple_stmt = 57, /* simple_stmt */ + YYSYMBOL_block_stmt = 58, /* block_stmt */ + YYSYMBOL_if_stmt = 59, /* if_stmt */ + YYSYMBOL_elif_tail = 60, /* elif_tail */ + YYSYMBOL_opt_else = 61, /* opt_else */ + YYSYMBOL_while_stmt = 62, /* while_stmt */ + YYSYMBOL_expr = 63, /* expr */ + YYSYMBOL_assign_expr = 64, /* assign_expr */ + YYSYMBOL_equality_expr = 65, /* equality_expr */ + YYSYMBOL_relational_expr = 66, /* relational_expr */ + YYSYMBOL_additive_expr = 67, /* additive_expr */ + YYSYMBOL_multiplicative_expr = 68, /* multiplicative_expr */ + YYSYMBOL_unary_expr = 69, /* unary_expr */ + YYSYMBOL_postfix_expr = 70, /* postfix_expr */ + YYSYMBOL_opt_args = 71, /* opt_args */ + YYSYMBOL_arg_list = 72, /* arg_list */ + YYSYMBOL_primary_expr = 73, /* primary_expr */ + YYSYMBOL_type_as_expr = 74, /* type_as_expr */ + YYSYMBOL_lvalue = 75 /* lvalue */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + . */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_uint8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if 1 + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* 1 */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 6 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 158 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 41 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 35 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 81 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 149 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 295 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40 +}; + +#if YYDEBUG +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 78, 78, 83, 84, 88, 102, 103, 107, 108, + 112, 113, 117, 119, 129, 131, 141, 143, 145, 150, + 163, 164, 168, 169, 173, 178, 188, 189, 190, 191, + 192, 193, 194, 195, 205, 206, 207, 208, 209, 210, + 214, 224, 231, 235, 242, 243, 247, 257, 261, 262, + 266, 267, 271, 272, 273, 277, 278, 279, 283, 284, + 285, 289, 290, 294, 296, 298, 300, 305, 306, 310, + 311, 315, 316, 317, 318, 319, 320, 321, 325, 330, + 331, 332 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if 1 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "CLASS", "VAR", "IS", + "END", "METHOD", "RETURN", "IF", "THEN", "ELSE", "ELSEIF", "WHILE", "DO", + "TRUE", "FALSE", "COLON", "SEMICOLON", "COMMA", "LPAREN", "RPAREN", + "LBRACE", "RBRACE", "LBRACKET", "RBRACKET", "ASSIGN", "ARROW", "PLUS", + "MINUS", "STAR", "SLASH", "DOT", "GT", "LT", "EQEQ", "EQUAL", + "IDENTIFIER", "TYPE_NAME", "INT_LITERAL", "STRING_LITERAL", "$accept", + "program", "class_list", "class_decl", "class_body", "member_list", + "member", "type_spec", "type_list", "var_decl", "method_decl", + "opt_params", "param_list", "param", "method_body", "stmt_list", + "simple_stmt", "block_stmt", "if_stmt", "elif_tail", "opt_else", + "while_stmt", "expr", "assign_expr", "equality_expr", "relational_expr", + "additive_expr", "multiplicative_expr", "unary_expr", "postfix_expr", + "opt_args", "arg_list", "primary_expr", "type_as_expr", "lvalue", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#define YYPACT_NINF (-130) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-80) + +#define yytable_value_is_error(Yyn) \ + 0 + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 15, -26, 24, 15, -130, 44, -130, -130, 8, 18, + 26, 63, 8, -130, -130, -130, 68, 51, -130, -130, + 50, 62, 57, -10, 91, 89, 108, -130, 50, -130, + 80, 80, 50, 111, 62, -130, -13, -130, -130, 80, + 106, 99, -130, -130, -130, 112, -130, 94, 34, 52, + 67, -130, 41, -130, -130, 40, 113, -130, 50, -130, + 50, -130, 115, -130, -130, -130, 106, 106, 106, 106, + 106, 106, 106, 80, 80, 95, 80, 80, 96, -130, + 107, -130, -130, 34, 52, 52, 67, 67, -130, -130, + -130, 116, 119, 114, -130, 117, -130, -130, 74, -130, + 80, -130, -130, 103, 80, 80, 80, -130, 74, 123, + -130, -130, -130, 60, -130, 130, -130, 138, 135, 132, + -130, -130, -130, 80, 50, 74, 74, -130, -130, -130, + -7, 74, 90, 19, 80, 80, 74, 80, 145, -130, + -130, -130, -130, -130, 142, -130, 74, 90, -130 +}; + +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 0, 0, 0, 2, 4, 0, 1, 3, 7, 0, + 0, 0, 6, 9, 10, 11, 0, 0, 5, 8, + 0, 21, 12, 0, 0, 0, 20, 23, 0, 16, + 0, 0, 0, 0, 0, 14, 0, 73, 74, 0, + 0, 75, 71, 72, 78, 0, 47, 49, 51, 54, + 57, 60, 62, 66, 76, 0, 0, 24, 0, 22, + 0, 13, 0, 75, 61, 18, 0, 0, 0, 0, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 17, + 0, 15, 77, 50, 52, 53, 55, 56, 58, 59, + 70, 0, 67, 0, 64, 0, 48, 80, 0, 63, + 0, 65, 81, 0, 0, 0, 0, 19, 25, 33, + 31, 32, 39, 0, 69, 0, 34, 0, 0, 29, + 27, 28, 30, 0, 0, 0, 0, 26, 38, 47, + 35, 40, 45, 0, 0, 0, 0, 0, 0, 43, + 46, 37, 36, 44, 0, 41, 0, 45, 42 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -130, -130, -130, 150, -130, -130, 143, -18, -130, -130, + -130, -130, -130, 120, -130, -93, -86, -129, -71, 9, + -130, -54, -30, -73, -130, 92, 48, 54, -19, -130, + -130, -130, -130, -130, -95 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_uint8 yydefgoto[] = +{ + 0, 2, 3, 4, 11, 12, 13, 44, 36, 14, + 15, 25, 26, 27, 107, 131, 109, 132, 110, 138, + 139, 111, 112, 46, 47, 48, 49, 50, 51, 52, + 91, 92, 53, 54, 55 +}; + +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 45, 56, 23, 113, 96, 108, 60, 143, 29, 62, + 35, 5, 9, 113, 57, 10, 30, 147, 1, 134, + 61, 64, 119, 103, 6, 140, 31, 104, 105, 135, + 113, 113, 106, 133, 37, 38, 113, 120, 113, 39, + 80, 113, 81, 90, 93, 119, 95, 119, 40, 8, + 129, 113, 88, 89, 121, 16, 41, 22, 42, 43, + 120, 73, 120, 17, 76, 74, 77, 67, 68, 18, + 114, 21, 78, 75, 116, 117, 118, 121, 103, 121, + 69, 70, 104, 105, 76, 20, 123, 106, 22, 37, + 38, 28, 78, 128, 39, 37, 38, 71, 72, 24, + 39, 136, 137, 40, 141, 142, 130, 144, 32, 40, + 33, 41, 22, 42, 43, 84, 85, 41, 22, 42, + 43, 37, 38, 86, 87, -79, 39, 34, 58, 66, + 65, 79, 94, 97, 98, 40, 82, 99, 100, 101, + 115, 122, 102, 63, 22, 42, 43, 124, 125, 126, + 127, 145, 146, 7, 59, 19, 148, 0, 83 +}; + +static const yytype_int16 yycheck[] = +{ + 30, 31, 20, 98, 77, 98, 19, 136, 18, 39, + 28, 37, 4, 108, 32, 7, 26, 146, 3, 26, + 33, 40, 108, 4, 0, 6, 36, 8, 9, 36, + 125, 126, 13, 126, 15, 16, 131, 108, 133, 20, + 58, 136, 60, 73, 74, 131, 76, 133, 29, 5, + 123, 146, 71, 72, 108, 37, 37, 38, 39, 40, + 131, 20, 133, 37, 24, 24, 26, 33, 34, 6, + 100, 20, 32, 32, 104, 105, 106, 131, 4, 133, + 28, 29, 8, 9, 24, 17, 26, 13, 38, 15, + 16, 34, 32, 123, 20, 15, 16, 30, 31, 37, + 20, 11, 12, 29, 134, 135, 124, 137, 17, 29, + 21, 37, 38, 39, 40, 67, 68, 37, 38, 39, + 40, 15, 16, 69, 70, 26, 20, 19, 17, 35, + 18, 18, 37, 37, 27, 29, 21, 21, 19, 25, + 37, 18, 25, 37, 38, 39, 40, 17, 10, 14, + 18, 6, 10, 3, 34, 12, 147, -1, 66 +}; + +/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 3, 42, 43, 44, 37, 0, 44, 5, 4, + 7, 45, 46, 47, 50, 51, 37, 37, 6, 47, + 17, 20, 38, 48, 37, 52, 53, 54, 34, 18, + 26, 36, 17, 21, 19, 48, 49, 15, 16, 20, + 29, 37, 39, 40, 48, 63, 64, 65, 66, 67, + 68, 69, 70, 73, 74, 75, 63, 48, 17, 54, + 19, 33, 63, 37, 69, 18, 35, 33, 34, 28, + 29, 30, 31, 20, 24, 32, 24, 26, 32, 18, + 48, 48, 21, 66, 67, 67, 68, 68, 69, 69, + 63, 71, 72, 63, 37, 63, 64, 37, 27, 21, + 19, 25, 25, 4, 8, 9, 13, 55, 56, 57, + 59, 62, 63, 75, 63, 37, 63, 63, 63, 57, + 59, 62, 18, 26, 17, 10, 14, 18, 63, 64, + 48, 56, 58, 56, 26, 36, 11, 12, 60, 61, + 6, 63, 63, 58, 63, 6, 10, 58, 60 +}; + +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr1[] = +{ + 0, 41, 42, 43, 43, 44, 45, 45, 46, 46, + 47, 47, 48, 48, 49, 49, 50, 50, 50, 51, + 52, 52, 53, 53, 54, 55, 56, 56, 56, 56, + 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, + 58, 59, 60, 60, 61, 61, 62, 63, 64, 64, + 65, 65, 66, 66, 66, 67, 67, 67, 68, 68, + 68, 69, 69, 70, 70, 70, 70, 71, 71, 72, + 72, 73, 73, 73, 73, 73, 73, 73, 74, 75, + 75, 75 +}; + +/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 2, 1, 5, 1, 0, 2, 1, + 1, 1, 1, 4, 1, 3, 5, 7, 7, 9, + 1, 0, 3, 1, 3, 1, 3, 2, 2, 2, + 2, 1, 1, 1, 2, 4, 6, 6, 3, 1, + 1, 6, 5, 1, 2, 0, 5, 1, 3, 1, + 3, 1, 3, 3, 1, 3, 3, 1, 3, 3, + 1, 2, 1, 4, 3, 4, 1, 1, 0, 3, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, + 3, 4 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + if (!yyvaluep) + return; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)]); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +/* Context of a parse error. */ +typedef struct +{ + yy_state_t *yyssp; + yysymbol_kind_t yytoken; +} yypcontext_t; + +/* Put in YYARG at most YYARGN of the expected tokens given the + current YYCTX, and return the number of tokens stored in YYARG. If + YYARG is null, return the number of expected tokens (guaranteed to + be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. + Return 0 if there are more than YYARGN expected tokens, yet fill + YYARG up to YYARGN. */ +static int +yypcontext_expected_tokens (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + int yyn = yypact[+*yyctx->yyssp]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); + } + } + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = YYSYMBOL_YYEMPTY; + return yycount; +} + + + + +#ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +#endif + +#ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +#endif + +#ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +#endif + + +static int +yy_syntax_error_arguments (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yyctx->yytoken != YYSYMBOL_YYEMPTY) + { + int yyn; + if (yyarg) + yyarg[yycount] = yyctx->yytoken; + ++yycount; + yyn = yypcontext_expected_tokens (yyctx, + yyarg ? yyarg + 1 : yyarg, yyargn - 1); + if (yyn == YYENOMEM) + return YYENOMEM; + else + yycount += yyn; + } + return yycount; +} + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + const yypcontext_t *yyctx) +{ + enum { YYARGS_MAX = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + yysymbol_kind_t yyarg[YYARGS_MAX]; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* Actual size of YYARG. */ + int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); + if (yycount == YYENOMEM) + return YYENOMEM; + + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + /* Compute error message size. Don't count the "%s"s, but reserve + room for the terminator. */ + yysize = yystrlen (yyformat) - 2 * yycount + 1; + { + int yyi; + for (yyi = 0; yyi < yycount; ++yyi) + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return YYENOMEM; + } + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return -1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep) +{ + YY_USE (yyvaluep); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/* Lookahead token kind. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void) +{ + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + YYNOMEM; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + YYNOMEM; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + YYNOMEM; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: /* program: class_list */ +#line 79 "parser.y" + { g_program = new AST::Program(); for (auto* c : *(yyvsp[0].classlist)) g_program->classes.push_back(c); delete (yyvsp[0].classlist); } +#line 1522 "parser.cpp" + break; + + case 3: /* class_list: class_list class_decl */ +#line 83 "parser.y" + { (yyval.classlist) = (yyvsp[-1].classlist); (yyvsp[-1].classlist)->push_back((yyvsp[0].classdecl)); } +#line 1528 "parser.cpp" + break; + + case 4: /* class_list: class_decl */ +#line 84 "parser.y" + { (yyval.classlist) = new std::vector(); (yyval.classlist)->push_back((yyvsp[0].classdecl)); } +#line 1534 "parser.cpp" + break; + + case 5: /* class_decl: CLASS IDENTIFIER IS class_body END */ +#line 89 "parser.y" + { + (yyval.classdecl) = new AST::ClassDecl((yyvsp[-3].cstr)); + for (auto* n : *(yyvsp[-1].memberlist)) { + if (auto* v = dynamic_cast(n)) (yyval.classdecl)->fields.push_back(v); + else if (auto* m = dynamic_cast(n)) (yyval.classdecl)->methods.push_back(m); + else delete n; + } + free((yyvsp[-3].cstr)); + delete (yyvsp[-1].memberlist); + } +#line 1549 "parser.cpp" + break; + + case 6: /* class_body: member_list */ +#line 102 "parser.y" + { (yyval.memberlist) = (yyvsp[0].memberlist); } +#line 1555 "parser.cpp" + break; + + case 7: /* class_body: %empty */ +#line 103 "parser.y" + { (yyval.memberlist) = new std::vector(); } +#line 1561 "parser.cpp" + break; + + case 8: /* member_list: member_list member */ +#line 107 "parser.y" + { (yyval.memberlist) = (yyvsp[-1].memberlist); (yyvsp[-1].memberlist)->push_back((yyvsp[0].node)); } +#line 1567 "parser.cpp" + break; + + case 9: /* member_list: member */ +#line 108 "parser.y" + { (yyval.memberlist) = new std::vector(); (yyval.memberlist)->push_back((yyvsp[0].node)); } +#line 1573 "parser.cpp" + break; + + case 10: /* member: var_decl */ +#line 112 "parser.y" + { (yyval.node) = (yyvsp[0].vardecl); } +#line 1579 "parser.cpp" + break; + + case 11: /* member: method_decl */ +#line 113 "parser.y" + { (yyval.node) = (yyvsp[0].methoddecl); } +#line 1585 "parser.cpp" + break; + + case 12: /* type_spec: TYPE_NAME */ +#line 118 "parser.y" + { (yyval.cstr) = (yyvsp[0].cstr); } +#line 1591 "parser.cpp" + break; + + case 13: /* type_spec: TYPE_NAME LT type_list GT */ +#line 120 "parser.y" + { + std::string s = std::string((yyvsp[-3].cstr)) + "<" + std::string((yyvsp[-1].cstr)) + ">"; + (yyval.cstr) = strdup(s.c_str()); + free((yyvsp[-3].cstr)); + free((yyvsp[-1].cstr)); + } +#line 1602 "parser.cpp" + break; + + case 14: /* type_list: type_spec */ +#line 130 "parser.y" + { (yyval.cstr) = (yyvsp[0].cstr); } +#line 1608 "parser.cpp" + break; + + case 15: /* type_list: type_list COMMA type_spec */ +#line 132 "parser.y" + { + std::string s = std::string((yyvsp[-2].cstr)) + "," + std::string((yyvsp[0].cstr)); + (yyval.cstr) = strdup(s.c_str()); + free((yyvsp[-2].cstr)); + free((yyvsp[0].cstr)); + } +#line 1619 "parser.cpp" + break; + + case 16: /* var_decl: VAR IDENTIFIER COLON type_spec SEMICOLON */ +#line 142 "parser.y" + { (yyval.vardecl) = new AST::VarDecl((yyvsp[-3].cstr), (yyvsp[-1].cstr), nullptr); free((yyvsp[-3].cstr)); free((yyvsp[-1].cstr)); } +#line 1625 "parser.cpp" + break; + + case 17: /* var_decl: VAR IDENTIFIER COLON type_spec EQUAL expr SEMICOLON */ +#line 144 "parser.y" + { (yyval.vardecl) = new AST::VarDecl((yyvsp[-5].cstr), (yyvsp[-3].cstr), (yyvsp[-1].expr)); free((yyvsp[-5].cstr)); free((yyvsp[-3].cstr)); } +#line 1631 "parser.cpp" + break; + + case 18: /* var_decl: VAR IDENTIFIER COLON type_spec ASSIGN expr SEMICOLON */ +#line 146 "parser.y" + { (yyval.vardecl) = new AST::VarDecl((yyvsp[-5].cstr), (yyvsp[-3].cstr), (yyvsp[-1].expr)); free((yyvsp[-5].cstr)); free((yyvsp[-3].cstr)); } +#line 1637 "parser.cpp" + break; + + case 19: /* method_decl: METHOD IDENTIFIER LPAREN opt_params RPAREN COLON type_spec ARROW method_body */ +#line 151 "parser.y" + { + (yyval.methoddecl) = new AST::MethodDecl((yyvsp[-7].cstr), (yyvsp[-2].cstr), (yyvsp[0].stmt)); + if ((yyvsp[-5].paramlist)) { + for (auto* p : *(yyvsp[-5].paramlist)) (yyval.methoddecl)->params.push_back(p); + delete (yyvsp[-5].paramlist); + } + free((yyvsp[-7].cstr)); + free((yyvsp[-2].cstr)); + } +#line 1651 "parser.cpp" + break; + + case 20: /* opt_params: param_list */ +#line 163 "parser.y" + { (yyval.paramlist) = (yyvsp[0].paramlist); } +#line 1657 "parser.cpp" + break; + + case 21: /* opt_params: %empty */ +#line 164 "parser.y" + { (yyval.paramlist) = new std::vector(); } +#line 1663 "parser.cpp" + break; + + case 22: /* param_list: param_list COMMA param */ +#line 168 "parser.y" + { (yyval.paramlist) = (yyvsp[-2].paramlist); (yyvsp[-2].paramlist)->push_back((yyvsp[0].param)); } +#line 1669 "parser.cpp" + break; + + case 23: /* param_list: param */ +#line 169 "parser.y" + { (yyval.paramlist) = new std::vector(); (yyval.paramlist)->push_back((yyvsp[0].param)); } +#line 1675 "parser.cpp" + break; + + case 24: /* param: IDENTIFIER COLON type_spec */ +#line 174 "parser.y" + { (yyval.param) = new AST::Param((yyvsp[-2].cstr), (yyvsp[0].cstr)); free((yyvsp[-2].cstr)); free((yyvsp[0].cstr)); } +#line 1681 "parser.cpp" + break; + + case 25: /* method_body: stmt_list */ +#line 179 "parser.y" + { + auto* b = new AST::Block(); + for (auto* s : *(yyvsp[0].stmtlist)) b->stmts.push_back(s); + delete (yyvsp[0].stmtlist); + (yyval.stmt) = b; + } +#line 1692 "parser.cpp" + break; + + case 26: /* stmt_list: stmt_list simple_stmt SEMICOLON */ +#line 188 "parser.y" + { (yyval.stmtlist) = (yyvsp[-2].stmtlist); (yyvsp[-2].stmtlist)->push_back((yyvsp[-1].stmt)); } +#line 1698 "parser.cpp" + break; + + case 27: /* stmt_list: stmt_list if_stmt */ +#line 189 "parser.y" + { (yyval.stmtlist) = (yyvsp[-1].stmtlist); (yyvsp[-1].stmtlist)->push_back((yyvsp[0].stmt)); } +#line 1704 "parser.cpp" + break; + + case 28: /* stmt_list: stmt_list while_stmt */ +#line 190 "parser.y" + { (yyval.stmtlist) = (yyvsp[-1].stmtlist); (yyvsp[-1].stmtlist)->push_back((yyvsp[0].stmt)); } +#line 1710 "parser.cpp" + break; + + case 29: /* stmt_list: stmt_list simple_stmt */ +#line 191 "parser.y" + { (yyval.stmtlist) = (yyvsp[-1].stmtlist); (yyvsp[-1].stmtlist)->push_back((yyvsp[0].stmt)); } +#line 1716 "parser.cpp" + break; + + case 30: /* stmt_list: simple_stmt SEMICOLON */ +#line 192 "parser.y" + { (yyval.stmtlist) = new std::vector(); (yyval.stmtlist)->push_back((yyvsp[-1].stmt)); } +#line 1722 "parser.cpp" + break; + + case 31: /* stmt_list: if_stmt */ +#line 193 "parser.y" + { (yyval.stmtlist) = new std::vector(); (yyval.stmtlist)->push_back((yyvsp[0].stmt)); } +#line 1728 "parser.cpp" + break; + + case 32: /* stmt_list: while_stmt */ +#line 194 "parser.y" + { (yyval.stmtlist) = new std::vector(); (yyval.stmtlist)->push_back((yyvsp[0].stmt)); } +#line 1734 "parser.cpp" + break; + + case 33: /* stmt_list: simple_stmt */ +#line 195 "parser.y" + { (yyval.stmtlist) = new std::vector(); (yyval.stmtlist)->push_back((yyvsp[0].stmt)); } +#line 1740 "parser.cpp" + break; + + case 34: /* simple_stmt: RETURN expr */ +#line 205 "parser.y" + { (yyval.stmt) = new AST::ReturnStmt((yyvsp[0].expr)); } +#line 1746 "parser.cpp" + break; + + case 35: /* simple_stmt: VAR IDENTIFIER COLON type_spec */ +#line 206 "parser.y" + { (yyval.stmt) = new AST::VarDeclStmt(new AST::VarDecl((yyvsp[-2].cstr), (yyvsp[0].cstr), nullptr)); free((yyvsp[-2].cstr)); free((yyvsp[0].cstr)); } +#line 1752 "parser.cpp" + break; + + case 36: /* simple_stmt: VAR IDENTIFIER COLON type_spec EQUAL expr */ +#line 207 "parser.y" + { (yyval.stmt) = new AST::VarDeclStmt(new AST::VarDecl((yyvsp[-4].cstr), (yyvsp[-2].cstr), (yyvsp[0].expr))); free((yyvsp[-4].cstr)); free((yyvsp[-2].cstr)); } +#line 1758 "parser.cpp" + break; + + case 37: /* simple_stmt: VAR IDENTIFIER COLON type_spec ASSIGN expr */ +#line 208 "parser.y" + { (yyval.stmt) = new AST::VarDeclStmt(new AST::VarDecl((yyvsp[-4].cstr), (yyvsp[-2].cstr), (yyvsp[0].expr))); free((yyvsp[-4].cstr)); free((yyvsp[-2].cstr)); } +#line 1764 "parser.cpp" + break; + + case 38: /* simple_stmt: lvalue ASSIGN expr */ +#line 209 "parser.y" + { (yyval.stmt) = new AST::ExprStmt(new AST::Binary(AST::BinOp::Assign, (yyvsp[-2].expr), (yyvsp[0].expr))); } +#line 1770 "parser.cpp" + break; + + case 39: /* simple_stmt: expr */ +#line 210 "parser.y" + { (yyval.stmt) = new AST::ExprStmt((yyvsp[0].expr)); } +#line 1776 "parser.cpp" + break; + + case 40: /* block_stmt: stmt_list */ +#line 215 "parser.y" + { + auto* b = new AST::Block(); + for (auto* s : *(yyvsp[0].stmtlist)) b->stmts.push_back(s); + delete (yyvsp[0].stmtlist); + (yyval.stmt) = b; + } +#line 1787 "parser.cpp" + break; + + case 41: /* if_stmt: IF expr THEN block_stmt elif_tail END */ +#line 225 "parser.y" + { + (yyval.stmt) = new AST::IfStmt((yyvsp[-4].expr), (yyvsp[-2].stmt), (yyvsp[-1].stmt)); + } +#line 1795 "parser.cpp" + break; + + case 42: /* elif_tail: ELSEIF expr THEN block_stmt elif_tail */ +#line 232 "parser.y" + { + (yyval.stmt) = new AST::IfStmt((yyvsp[-3].expr), (yyvsp[-1].stmt), (yyvsp[0].stmt)); + } +#line 1803 "parser.cpp" + break; + + case 43: /* elif_tail: opt_else */ +#line 236 "parser.y" + { + (yyval.stmt) = (yyvsp[0].stmt) ? (yyvsp[0].stmt) : static_cast(new AST::Block()); + } +#line 1811 "parser.cpp" + break; + + case 44: /* opt_else: ELSE block_stmt */ +#line 242 "parser.y" + { (yyval.stmt) = (yyvsp[0].stmt); } +#line 1817 "parser.cpp" + break; + + case 45: /* opt_else: %empty */ +#line 243 "parser.y" + { (yyval.stmt) = nullptr; } +#line 1823 "parser.cpp" + break; + + case 46: /* while_stmt: WHILE expr DO stmt_list END */ +#line 248 "parser.y" + { + auto* b = new AST::Block(); + for (auto* s : *(yyvsp[-1].stmtlist)) b->stmts.push_back(s); + delete (yyvsp[-1].stmtlist); + (yyval.stmt) = new AST::WhileStmt((yyvsp[-3].expr), b); + } +#line 1834 "parser.cpp" + break; + + case 47: /* expr: assign_expr */ +#line 257 "parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 1840 "parser.cpp" + break; + + case 48: /* assign_expr: lvalue ASSIGN assign_expr */ +#line 261 "parser.y" + { (yyval.expr) = new AST::Binary(AST::BinOp::Assign, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 1846 "parser.cpp" + break; + + case 49: /* assign_expr: equality_expr */ +#line 262 "parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 1852 "parser.cpp" + break; + + case 50: /* equality_expr: equality_expr EQEQ relational_expr */ +#line 266 "parser.y" + { (yyval.expr) = new AST::Binary(AST::BinOp::Eq, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 1858 "parser.cpp" + break; + + case 51: /* equality_expr: relational_expr */ +#line 267 "parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 1864 "parser.cpp" + break; + + case 52: /* relational_expr: relational_expr GT additive_expr */ +#line 271 "parser.y" + { (yyval.expr) = new AST::Binary(AST::BinOp::Gt, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 1870 "parser.cpp" + break; + + case 53: /* relational_expr: relational_expr LT additive_expr */ +#line 272 "parser.y" + { (yyval.expr) = new AST::Binary(AST::BinOp::Lt, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 1876 "parser.cpp" + break; + + case 54: /* relational_expr: additive_expr */ +#line 273 "parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 1882 "parser.cpp" + break; + + case 55: /* additive_expr: additive_expr PLUS multiplicative_expr */ +#line 277 "parser.y" + { (yyval.expr) = new AST::Binary(AST::BinOp::Add, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 1888 "parser.cpp" + break; + + case 56: /* additive_expr: additive_expr MINUS multiplicative_expr */ +#line 278 "parser.y" + { (yyval.expr) = new AST::Binary(AST::BinOp::Sub, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 1894 "parser.cpp" + break; + + case 57: /* additive_expr: multiplicative_expr */ +#line 279 "parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 1900 "parser.cpp" + break; + + case 58: /* multiplicative_expr: multiplicative_expr STAR unary_expr */ +#line 283 "parser.y" + { (yyval.expr) = new AST::Binary(AST::BinOp::Mul, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 1906 "parser.cpp" + break; + + case 59: /* multiplicative_expr: multiplicative_expr SLASH unary_expr */ +#line 284 "parser.y" + { (yyval.expr) = new AST::Binary(AST::BinOp::Div, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 1912 "parser.cpp" + break; + + case 60: /* multiplicative_expr: unary_expr */ +#line 285 "parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 1918 "parser.cpp" + break; + + case 61: /* unary_expr: MINUS unary_expr */ +#line 289 "parser.y" + { (yyval.expr) = new AST::Unary(AST::Unary::Op::Neg, (yyvsp[0].expr)); } +#line 1924 "parser.cpp" + break; + + case 62: /* unary_expr: postfix_expr */ +#line 290 "parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 1930 "parser.cpp" + break; + + case 63: /* postfix_expr: postfix_expr LPAREN opt_args RPAREN */ +#line 295 "parser.y" + { auto* call = new AST::Call((yyvsp[-3].expr)); for (auto* e : *(yyvsp[-1].exprlist)) call->args.push_back(e); delete (yyvsp[-1].exprlist); (yyval.expr) = call; } +#line 1936 "parser.cpp" + break; + + case 64: /* postfix_expr: postfix_expr DOT IDENTIFIER */ +#line 297 "parser.y" + { (yyval.expr) = new AST::MemberAccess((yyvsp[-2].expr), (yyvsp[0].cstr)); free((yyvsp[0].cstr)); } +#line 1942 "parser.cpp" + break; + + case 65: /* postfix_expr: postfix_expr LBRACKET expr RBRACKET */ +#line 299 "parser.y" + { (yyval.expr) = new AST::Index((yyvsp[-3].expr), (yyvsp[-1].expr)); } +#line 1948 "parser.cpp" + break; + + case 66: /* postfix_expr: primary_expr */ +#line 301 "parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 1954 "parser.cpp" + break; + + case 67: /* opt_args: arg_list */ +#line 305 "parser.y" + { (yyval.exprlist) = (yyvsp[0].exprlist); } +#line 1960 "parser.cpp" + break; + + case 68: /* opt_args: %empty */ +#line 306 "parser.y" + { (yyval.exprlist) = new std::vector(); } +#line 1966 "parser.cpp" + break; + + case 69: /* arg_list: arg_list COMMA expr */ +#line 310 "parser.y" + { (yyval.exprlist) = (yyvsp[-2].exprlist); (yyvsp[-2].exprlist)->push_back((yyvsp[0].expr)); } +#line 1972 "parser.cpp" + break; + + case 70: /* arg_list: expr */ +#line 311 "parser.y" + { (yyval.exprlist) = new std::vector(); (yyval.exprlist)->push_back((yyvsp[0].expr)); } +#line 1978 "parser.cpp" + break; + + case 71: /* primary_expr: INT_LITERAL */ +#line 315 "parser.y" + { (yyval.expr) = new AST::IntLiteral((yyvsp[0].ival)); } +#line 1984 "parser.cpp" + break; + + case 72: /* primary_expr: STRING_LITERAL */ +#line 316 "parser.y" + { (yyval.expr) = new AST::StringLiteral((yyvsp[0].cstr)); free((yyvsp[0].cstr)); } +#line 1990 "parser.cpp" + break; + + case 73: /* primary_expr: TRUE */ +#line 317 "parser.y" + { (yyval.expr) = new AST::BoolLiteral(true); } +#line 1996 "parser.cpp" + break; + + case 74: /* primary_expr: FALSE */ +#line 318 "parser.y" + { (yyval.expr) = new AST::BoolLiteral(false); } +#line 2002 "parser.cpp" + break; + + case 75: /* primary_expr: IDENTIFIER */ +#line 319 "parser.y" + { (yyval.expr) = new AST::Identifier((yyvsp[0].cstr)); free((yyvsp[0].cstr)); } +#line 2008 "parser.cpp" + break; + + case 76: /* primary_expr: type_as_expr */ +#line 320 "parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 2014 "parser.cpp" + break; + + case 77: /* primary_expr: LPAREN expr RPAREN */ +#line 321 "parser.y" + { (yyval.expr) = (yyvsp[-1].expr); } +#line 2020 "parser.cpp" + break; + + case 78: /* type_as_expr: type_spec */ +#line 326 "parser.y" + { (yyval.expr) = new AST::Identifier((yyvsp[0].cstr)); free((yyvsp[0].cstr)); } +#line 2026 "parser.cpp" + break; + + case 79: /* lvalue: IDENTIFIER */ +#line 330 "parser.y" + { (yyval.expr) = new AST::Identifier((yyvsp[0].cstr)); free((yyvsp[0].cstr)); } +#line 2032 "parser.cpp" + break; + + case 80: /* lvalue: lvalue DOT IDENTIFIER */ +#line 331 "parser.y" + { (yyval.expr) = new AST::MemberAccess((yyvsp[-2].expr), (yyvsp[0].cstr)); free((yyvsp[0].cstr)); } +#line 2038 "parser.cpp" + break; + + case 81: /* lvalue: lvalue LBRACKET expr RBRACKET */ +#line 332 "parser.y" + { (yyval.expr) = new AST::Index((yyvsp[-3].expr), (yyvsp[-1].expr)); } +#line 2044 "parser.cpp" + break; + + +#line 2048 "parser.cpp" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + { + yypcontext_t yyctx + = {yyssp, yytoken}; + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == -1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (yymsg) + { + yysyntax_error_status + = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + yymsgp = yymsg; + } + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = YYENOMEM; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == YYENOMEM) + YYNOMEM; + } + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + ++yynerrs; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturnlab; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturnlab; + + +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + goto yyreturnlab; + + +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + return yyresult; +} + +#line 335 "parser.y" + + +void yyerror(const char* s) { std::fprintf(stderr, "Parse error at line %d: %s\n", yylineno, s); } diff --git a/parser.hpp b/parser.hpp new file mode 100644 index 0000000..8d37f6a --- /dev/null +++ b/parser.hpp @@ -0,0 +1,147 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_YY_PARSER_HPP_INCLUDED +# define YY_YY_PARSER_HPP_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif +/* "%code requires" blocks. */ +#line 17 "parser.y" + + #include + namespace AST { + struct Node; struct Program; struct ClassDecl; struct VarDecl; struct Expr; struct Stmt; struct MethodDecl; struct Param; struct Block; + } + +#line 56 "parser.hpp" + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + CLASS = 258, /* CLASS */ + VAR = 259, /* VAR */ + IS = 260, /* IS */ + END = 261, /* END */ + METHOD = 262, /* METHOD */ + RETURN = 263, /* RETURN */ + IF = 264, /* IF */ + THEN = 265, /* THEN */ + ELSE = 266, /* ELSE */ + ELSEIF = 267, /* ELSEIF */ + WHILE = 268, /* WHILE */ + DO = 269, /* DO */ + TRUE = 270, /* TRUE */ + FALSE = 271, /* FALSE */ + COLON = 272, /* COLON */ + SEMICOLON = 273, /* SEMICOLON */ + COMMA = 274, /* COMMA */ + LPAREN = 275, /* LPAREN */ + RPAREN = 276, /* RPAREN */ + LBRACE = 277, /* LBRACE */ + RBRACE = 278, /* RBRACE */ + LBRACKET = 279, /* LBRACKET */ + RBRACKET = 280, /* RBRACKET */ + ASSIGN = 281, /* ASSIGN */ + ARROW = 282, /* ARROW */ + PLUS = 283, /* PLUS */ + MINUS = 284, /* MINUS */ + STAR = 285, /* STAR */ + SLASH = 286, /* SLASH */ + DOT = 287, /* DOT */ + GT = 288, /* GT */ + LT = 289, /* LT */ + EQEQ = 290, /* EQEQ */ + EQUAL = 291, /* EQUAL */ + IDENTIFIER = 292, /* IDENTIFIER */ + TYPE_NAME = 293, /* TYPE_NAME */ + INT_LITERAL = 294, /* INT_LITERAL */ + STRING_LITERAL = 295 /* STRING_LITERAL */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 27 "parser.y" + + long long ival; + char* cstr; + AST::Program* program; + AST::ClassDecl* classdecl; + AST::VarDecl* vardecl; + AST::Expr* expr; + AST::Stmt* stmt; + AST::MethodDecl* methoddecl; + AST::Param* param; + AST::Node* node; + std::vector* classlist; + std::vector* memberlist; + std::vector* varlist; + std::vector* paramlist; + std::vector* stmtlist; + std::vector* exprlist; + std::vector* strlist; + +#line 133 "parser.hpp" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + + +int yyparse (void); + + +#endif /* !YY_YY_PARSER_HPP_INCLUDED */ diff --git a/parser.y b/parser.y index 06c0439..bff846a 100644 --- a/parser.y +++ b/parser.y @@ -17,14 +17,7 @@ AST::Program* g_program = nullptr; %code requires { #include namespace AST { - struct Node; - struct Program; - struct ClassDecl; - struct VarDecl; - struct Expr; - struct Stmt; - struct MethodDecl; - struct Param; + struct Node; struct Program; struct ClassDecl; struct VarDecl; struct Expr; struct Stmt; struct MethodDecl; struct Param; struct Block; } } @@ -32,31 +25,37 @@ AST::Program* g_program = nullptr; %define parse.error verbose %union { - long long ival; - char* cstr; - AST::Program* program; - AST::ClassDecl* classdecl; - AST::VarDecl* vardecl; - AST::Expr* expr; - AST::Stmt* stmt; - AST::MethodDecl* methoddecl; - AST::Param* param; - AST::Node* node; - std::vector* classlist; - std::vector* memberlist; - std::vector* varlist; - std::vector* paramlist; + long long ival; + char* cstr; + AST::Program* program; + AST::ClassDecl* classdecl; + AST::VarDecl* vardecl; + AST::Expr* expr; + AST::Stmt* stmt; + AST::MethodDecl* methoddecl; + AST::Param* param; + AST::Node* node; + std::vector* classlist; + std::vector* memberlist; + std::vector* varlist; + std::vector* paramlist; + std::vector* stmtlist; + std::vector* exprlist; + std::vector* strlist; } %token CLASS VAR IS END -%token METHOD RETURN IF THEN ELSE +%token METHOD RETURN IF THEN ELSE ELSEIF +%token WHILE DO %token TRUE FALSE %token COLON SEMICOLON COMMA -%token LPAREN RPAREN LBRACE RBRACE +%token LPAREN RPAREN LBRACE RBRACE LBRACKET RBRACKET %token ASSIGN ARROW PLUS MINUS STAR SLASH +%token DOT GT LT EQEQ EQUAL %token IDENTIFIER %token TYPE_NAME %token INT_LITERAL +%token STRING_LITERAL %type program %type class_list @@ -67,25 +66,22 @@ AST::Program* g_program = nullptr; %type method_decl %type opt_params param_list %type param -%type method_body stmt if_stmt -%type expr additive_expr multiplicative_expr unary_expr primary_expr +%type type_spec type_list +%type method_body stmt simple_stmt if_stmt while_stmt block_stmt elif_tail opt_else +%type stmt_list +%type expr assign_expr equality_expr relational_expr additive_expr multiplicative_expr unary_expr postfix_expr primary_expr lvalue type_as_expr +%type arg_list opt_args %% program : class_list - { - g_program = new AST::Program(); - for (auto* c : *$1) g_program->classes.push_back(c); - delete $1; - } + { g_program = new AST::Program(); for (auto* c : *$1) g_program->classes.push_back(c); delete $1; } ; class_list - : class_list class_decl - { $$ = $1; $1->push_back($2); } - | class_decl - { $$ = new std::vector(); $$->push_back($1); } + : class_list class_decl { $$ = $1; $1->push_back($2); } + | class_decl { $$ = new std::vector(); $$->push_back($1); } ; class_decl @@ -93,9 +89,9 @@ class_decl { $$ = new AST::ClassDecl($2); for (auto* n : *$4) { - if (auto* v = dynamic_cast(n)) $$->fields.push_back(v); - else if (auto* m = dynamic_cast(n)) $$->methods.push_back(m); - else delete n; + if (auto* v = dynamic_cast(n)) $$->fields.push_back(v); + else if (auto* m = dynamic_cast(n)) $$->methods.push_back(m); + else delete n; } free($2); delete $4; @@ -104,126 +100,238 @@ class_decl class_body : member_list { $$ = $1; } - | { $$ = new std::vector(); } + | { $$ = new std::vector(); } ; member_list - : member_list member - { $$ = $1; $1->push_back($2); } - | member - { $$ = new std::vector(); $$->push_back($1); } + : member_list member { $$ = $1; $1->push_back($2); } + | member { $$ = new std::vector(); $$->push_back($1); } ; member - : var_decl { $$ = $1; } - | method_decl { $$ = $1; } + : var_decl { $$ = $1; } + | method_decl { $$ = $1; } ; -var_decl - : VAR IDENTIFIER COLON TYPE_NAME SEMICOLON +type_spec + : TYPE_NAME + { $$ = $1; } + | TYPE_NAME LT type_list GT { - $$ = new AST::VarDecl($2, $4, nullptr); - free($2); free($4); + std::string s = std::string($1) + "<" + std::string($3) + ">"; + $$ = strdup(s.c_str()); + free($1); + free($3); } - | VAR IDENTIFIER COLON TYPE_NAME ASSIGN expr SEMICOLON + ; + +type_list + : type_spec + { $$ = $1; } + | type_list COMMA type_spec { - $$ = new AST::VarDecl($2, $4, $6); - free($2); free($4); + std::string s = std::string($1) + "," + std::string($3); + $$ = strdup(s.c_str()); + free($1); + free($3); } ; +var_decl + : VAR IDENTIFIER COLON type_spec SEMICOLON + { $$ = new AST::VarDecl($2, $4, nullptr); free($2); free($4); } + | VAR IDENTIFIER COLON type_spec EQUAL expr SEMICOLON + { $$ = new AST::VarDecl($2, $4, $6); free($2); free($4); } + | VAR IDENTIFIER COLON type_spec ASSIGN expr SEMICOLON + { $$ = new AST::VarDecl($2, $4, $6); free($2); free($4); } + ; + method_decl - : METHOD IDENTIFIER LPAREN opt_params RPAREN COLON TYPE_NAME ARROW method_body + : METHOD IDENTIFIER LPAREN opt_params RPAREN COLON type_spec ARROW method_body { $$ = new AST::MethodDecl($2, $7, $9); - if ($4) { for (auto* p : *$4) $$->params.push_back(p); delete $4; } - free($2); free($7); + if ($4) { + for (auto* p : *$4) $$->params.push_back(p); + delete $4; + } + free($2); + free($7); } ; opt_params : param_list { $$ = $1; } - | { $$ = new std::vector(); } + | { $$ = new std::vector(); } ; param_list - : param_list COMMA param - { $$ = $1; $1->push_back($3); } - | param - { $$ = new std::vector(); $$->push_back($1); } + : param_list COMMA param { $$ = $1; $1->push_back($3); } + | param { $$ = new std::vector(); $$->push_back($1); } ; param - : IDENTIFIER COLON TYPE_NAME + : IDENTIFIER COLON type_spec + { $$ = new AST::Param($1, $3); free($1); free($3); } + ; + +method_body + : stmt_list { - $$ = new AST::Param($1, $3); - free($1); free($3); + auto* b = new AST::Block(); + for (auto* s : *$1) b->stmts.push_back(s); + delete $1; + $$ = b; } ; -method_body - : expr - { $$ = new AST::ReturnStmt($1); } - | stmt - { $$ = $1; } +stmt_list + : stmt_list simple_stmt SEMICOLON { $$ = $1; $1->push_back($2); } + | stmt_list if_stmt { $$ = $1; $1->push_back($2); } + | stmt_list while_stmt { $$ = $1; $1->push_back($2); } + | stmt_list simple_stmt { $$ = $1; $1->push_back($2); } + | simple_stmt SEMICOLON { $$ = new std::vector(); $$->push_back($1); } + | if_stmt { $$ = new std::vector(); $$->push_back($1); } + | while_stmt { $$ = new std::vector(); $$->push_back($1); } + | simple_stmt { $$ = new std::vector(); $$->push_back($1); } ; stmt - : RETURN expr - { $$ = new AST::ReturnStmt($2); } - | if_stmt - { $$ = $1; } + : simple_stmt { $$ = $1; } + | if_stmt { $$ = $1; } + | while_stmt { $$ = $1; } + ; + +simple_stmt + : RETURN expr { $$ = new AST::ReturnStmt($2); } + | VAR IDENTIFIER COLON type_spec { $$ = new AST::VarDeclStmt(new AST::VarDecl($2, $4, nullptr)); free($2); free($4); } + | VAR IDENTIFIER COLON type_spec EQUAL expr { $$ = new AST::VarDeclStmt(new AST::VarDecl($2, $4, $6)); free($2); free($4); } + | VAR IDENTIFIER COLON type_spec ASSIGN expr { $$ = new AST::VarDeclStmt(new AST::VarDecl($2, $4, $6)); free($2); free($4); } + | lvalue ASSIGN expr { $$ = new AST::ExprStmt(new AST::Binary(AST::BinOp::Assign, $1, $3)); } + | expr { $$ = new AST::ExprStmt($1); } + ; + +block_stmt + : stmt_list + { + auto* b = new AST::Block(); + for (auto* s : *$1) b->stmts.push_back(s); + delete $1; + $$ = b; + } ; if_stmt - : IF expr THEN stmt ELSE stmt - { $$ = new AST::IfStmt($2, $4, $6); } + : IF expr THEN block_stmt elif_tail END + { + $$ = new AST::IfStmt($2, $4, $5); + } + ; + +elif_tail + : ELSEIF expr THEN block_stmt elif_tail + { + $$ = new AST::IfStmt($2, $4, $5); + } + | opt_else + { + $$ = $1 ? $1 : static_cast(new AST::Block()); + } + ; + +opt_else + : ELSE block_stmt { $$ = $2; } + | { $$ = nullptr; } + ; + +while_stmt + : WHILE expr DO stmt_list END + { + auto* b = new AST::Block(); + for (auto* s : *$4) b->stmts.push_back(s); + delete $4; + $$ = new AST::WhileStmt($2, b); + } ; expr - : additive_expr { $$ = $1; } + : assign_expr { $$ = $1; } + ; + +assign_expr + : lvalue ASSIGN assign_expr { $$ = new AST::Binary(AST::BinOp::Assign, $1, $3); } + | equality_expr { $$ = $1; } + ; + +equality_expr + : equality_expr EQEQ relational_expr { $$ = new AST::Binary(AST::BinOp::Eq, $1, $3); } + | relational_expr { $$ = $1; } + ; + +relational_expr + : relational_expr GT additive_expr { $$ = new AST::Binary(AST::BinOp::Gt, $1, $3); } + | relational_expr LT additive_expr { $$ = new AST::Binary(AST::BinOp::Lt, $1, $3); } + | additive_expr { $$ = $1; } ; additive_expr - : additive_expr PLUS multiplicative_expr - { $$ = new AST::Binary(AST::BinOp::Add, $1, $3); } - | additive_expr MINUS multiplicative_expr - { $$ = new AST::Binary(AST::BinOp::Sub, $1, $3); } - | multiplicative_expr - { $$ = $1; } + : additive_expr PLUS multiplicative_expr { $$ = new AST::Binary(AST::BinOp::Add, $1, $3); } + | additive_expr MINUS multiplicative_expr { $$ = new AST::Binary(AST::BinOp::Sub, $1, $3); } + | multiplicative_expr { $$ = $1; } ; multiplicative_expr - : multiplicative_expr STAR unary_expr - { $$ = new AST::Binary(AST::BinOp::Mul, $1, $3); } - | multiplicative_expr SLASH unary_expr - { $$ = new AST::Binary(AST::BinOp::Div, $1, $3); } - | unary_expr - { $$ = $1; } + : multiplicative_expr STAR unary_expr { $$ = new AST::Binary(AST::BinOp::Mul, $1, $3); } + | multiplicative_expr SLASH unary_expr { $$ = new AST::Binary(AST::BinOp::Div, $1, $3); } + | unary_expr { $$ = $1; } ; unary_expr - : MINUS unary_expr - { $$ = new AST::Unary(AST::Unary::Op::Neg, $2); } + : MINUS unary_expr { $$ = new AST::Unary(AST::Unary::Op::Neg, $2); } + | postfix_expr { $$ = $1; } + ; + +postfix_expr + : postfix_expr LPAREN opt_args RPAREN + { auto* call = new AST::Call($1); for (auto* e : *$3) call->args.push_back(e); delete $3; $$ = call; } + | postfix_expr DOT IDENTIFIER + { $$ = new AST::MemberAccess($1, $3); free($3); } + | postfix_expr LBRACKET expr RBRACKET + { $$ = new AST::Index($1, $3); } | primary_expr { $$ = $1; } ; +opt_args + : arg_list { $$ = $1; } + | { $$ = new std::vector(); } + ; + +arg_list + : arg_list COMMA expr { $$ = $1; $1->push_back($3); } + | expr { $$ = new std::vector(); $$->push_back($1); } + ; + primary_expr - : INT_LITERAL - { $$ = new AST::IntLiteral($1); } - | TRUE - { $$ = new AST::BoolLiteral(true); } - | FALSE - { $$ = new AST::BoolLiteral(false); } - | IDENTIFIER + : INT_LITERAL { $$ = new AST::IntLiteral($1); } + | STRING_LITERAL { $$ = new AST::StringLiteral($1); free($1); } + | TRUE { $$ = new AST::BoolLiteral(true); } + | FALSE { $$ = new AST::BoolLiteral(false); } + | IDENTIFIER { $$ = new AST::Identifier($1); free($1); } + | type_as_expr { $$ = $1; } + | LPAREN expr RPAREN { $$ = $2; } + ; + +type_as_expr + : type_spec { $$ = new AST::Identifier($1); free($1); } - | LPAREN expr RPAREN - { $$ = $2; } + ; + +lvalue + : IDENTIFIER { $$ = new AST::Identifier($1); free($1); } + | lvalue DOT IDENTIFIER { $$ = new AST::MemberAccess($1, $3); free($3); } + | lvalue LBRACKET expr RBRACKET { $$ = new AST::Index($1, $3); } ; %% -void yyerror(const char* s) { - std::fprintf(stderr, "Parse error at line %d: %s\n", yylineno, s); -} +void yyerror(const char* s) { std::fprintf(stderr, "Parse error at line %d: %s\n", yylineno, s); } diff --git a/semantic.cpp b/semantic.cpp new file mode 100644 index 0000000..3df43f4 --- /dev/null +++ b/semantic.cpp @@ -0,0 +1,468 @@ +#include "semantic.hpp" +#include +#include +#include + +std::string SemanticAnalyzer::curPos(AST::Node* n) { + (void)n; + return ""; +} + +SemanticAnalyzer::SemanticAnalyzer() { + classes.clear(); + curClass = nullptr; + curMethod = nullptr; + localsStack.clear(); +} + +void SemanticAnalyzer::indexClasses(AST::Program* p) { + for (auto* c : p->classes) { + classes[c->name] = c; + } +} + +SemanticResult SemanticAnalyzer::analyze(AST::Program* p) { + result = SemanticResult(); + indexClasses(p); + for (auto* c : p->classes) analyzeClass(c); + return result; +} + +void SemanticAnalyzer::analyzeClass(AST::ClassDecl* c) { + curClass = c; + for (auto* m : c->methods) analyzeMethod(m); + curClass = nullptr; +} + +void SemanticAnalyzer::analyzeMethod(AST::MethodDecl* m) { + curMethod = m; + pushScope(); + declaredLocals.clear(); + usedLocals.clear(); + for (auto* p : m->params) { + declareLocal(p->name, p->typeName); + } + for (auto* f : curClass->fields) { + declareLocal(f->name, f->typeName); + } + if (m->body) { + analyzeBlock(dynamic_cast(m->body)); + } else { + result.addError("Method " + m->name + " has empty body"); + } + for (const auto& name : declaredLocals) { + bool used = false; + for (const auto& u : usedLocals) { + if (u == name) { + used = true; + break; + } + } + if (!used) { + result.warnings.push_back("Variable '" + name + "' is never used in method '" + m->name + "'"); + result.optimizations.push_back("Variable '" + name + "' is unused and can be removed in method '" + m->name + "'"); + } + } + popScope(); + curMethod = nullptr; +} + +void SemanticAnalyzer::pushScope() { + localsStack.emplace_back(); +} + +void SemanticAnalyzer::popScope() { + if (!localsStack.empty()) localsStack.pop_back(); +} + +void SemanticAnalyzer::declareLocal(const std::string& name, const std::string& type) { + if (localsStack.empty()) pushScope(); + localsStack.back()[name] = type; +} + +std::string SemanticAnalyzer::typeOfExpr(AST::Expr* e) { + if (!e) return ""; + if (auto* il = dynamic_cast(e)) return std::string("Int"); + if (auto* sl = dynamic_cast(e)) return std::string("String"); + if (auto* bl = dynamic_cast(e)) return std::string("Bool"); + if (auto* id = dynamic_cast(e)) { + for (auto it = localsStack.rbegin(); it != localsStack.rend(); ++it) { + auto found = it->find(id->name); + if (found != it->end()) return found->second; + } + result.addError("Use of undeclared identifier '" + id->name + "'"); + return ""; + } + if (auto* bin = dynamic_cast(e)) { + auto lhs = typeOfExpr(bin->lhs); + auto rhs = typeOfExpr(bin->rhs); + switch (bin->op) { + case AST::BinOp::Add: case AST::BinOp::Sub: case AST::BinOp::Mul: case AST::BinOp::Div: + if (lhs != "Int" || rhs != "Int") { + result.addError("Arithmetic operator used with non-Int operands"); + return ""; + } + return "Int"; + case AST::BinOp::Gt: case AST::BinOp::Lt: + if (lhs != "Int" || rhs != "Int") { + result.addError("Relational operator used with non-Int operands"); + return ""; + } + return "Bool"; + case AST::BinOp::Eq: + if (lhs == "" || rhs == "" || lhs != rhs) { + result.addError("Equality operator used with incompatible types"); + return ""; + } + return "Bool"; + case AST::BinOp::Assign: + if (auto* lid = dynamic_cast(bin->lhs)) { + std::string lhs_type; + for (auto it = localsStack.rbegin(); it != localsStack.rend(); ++it) { + auto found = it->find(lid->name); + if (found != it->end()) { lhs_type = found->second; break; } + } + if (lhs_type.empty()) { + result.addError("Assignment to undeclared variable '" + lid->name + "'"); + return ""; + } + if (lhs_type != rhs) { + result.addError("Type mismatch in assignment to '" + lid->name + "'"); + return ""; + } + return lhs_type; + } + return rhs; + } + } + if (auto* un = dynamic_cast(e)) { + auto rt = typeOfExpr(un->rhs); + if (un->op == AST::Unary::Op::Neg) { + if (rt != "Int") { + result.addError("Unary - applied to non-Int"); + return ""; + } + return "Int"; + } + } + if (auto* call = dynamic_cast(e)) { + if (auto* id = dynamic_cast(call->callee)) { + if (id->name == "output" || id->name == "print") { + for (auto* a : call->args) typeOfExpr(a); + return ""; + } + auto it = classes.find(curClass->name); + if (it != classes.end()) { + bool found = false; + for (auto* m : it->second->methods) { + if (m->name == id->name) { found = true; break; } + } + if (!found) { + result.addError("Call to undeclared method '" + id->name + "' in class '" + curClass->name + "'"); + } + } + return ""; + } + return ""; + } + if (auto* ma = dynamic_cast(e)) { + if (auto* objid = dynamic_cast(ma->object)) { + std::string t; + for (auto it = localsStack.rbegin(); it != localsStack.rend(); ++it) { + auto found = it->find(objid->name); + if (found != it->end()) { t = found->second; break; } + } + if (t.empty()) { + result.addError("Unknown object '" + objid->name + "' for member access"); + return ""; + } + auto cit = classes.find(t); + if (cit != classes.end()) { + for (auto* f : cit->second->fields) if (f->name == ma->member) return f->typeName; + } + } + return ""; + } + if (auto* idx = dynamic_cast(e)) { + typeOfExpr(idx->base); + typeOfExpr(idx->index); + return ""; + } + return ""; +} + +void SemanticAnalyzer::analyzeBlock(AST::Block* b) { + if (!b) return; + for (auto*& s : b->stmts) { + analyzeStmt(s); + } + removeUnreachableInBlock(b); +} + +void SemanticAnalyzer::analyzeStmt(AST::Stmt*& s) { + if (!s) return; + if (auto* ifs = dynamic_cast(s)) { + analyzeExpr(ifs->cond); + analyzeStmt(ifs->thenS); + analyzeStmt(ifs->elseS); + simplifyIf(s); + return; + } + if (auto* w = dynamic_cast(s)) { + analyzeExpr(w->cond); + analyzeBlock(dynamic_cast(w->body)); + return; + } + if (auto* ret = dynamic_cast(s)) { + if (!curMethod) { + result.addError("Return used outside of method"); + } else { + analyzeExpr(ret->value); + auto rv = typeOfExpr(ret->value); + if (!curMethod->returnType.empty()) { + if (rv != "" && curMethod->returnType != rv) { + result.addError("Return type mismatch in method '" + curMethod->name + "': expected " + curMethod->returnType + ", got " + rv); + } + } + } + return; + } + if (auto* es = dynamic_cast(s)) { + analyzeExpr(es->expr); + return; + } + if (auto* vds = dynamic_cast(s)) { + if (vds->decl) { + declareLocal(vds->decl->name, vds->decl->typeName); + declaredLocals.push_back(vds->decl->name); + if (vds->decl->init) analyzeExpr(vds->decl->init); + if (vds->decl->init) { + auto it = typeOfExpr(vds->decl->init); + if (it != "" && it != vds->decl->typeName) { + result.addError("Initializer type mismatch for variable '" + vds->decl->name + "'"); + } + } + } + return; + } + if (auto* blk = dynamic_cast(s)) { + pushScope(); + analyzeBlock(blk); + popScope(); + return; + } +} + +void SemanticAnalyzer::analyzeExpr(AST::Expr*& e) { + if (!e) return; + bool folded = foldConstantsInExpr(e); + if (folded) { + } + if (auto* bin = dynamic_cast(e)) { + analyzeExpr(bin->lhs); + analyzeExpr(bin->rhs); + if (bin->op == AST::BinOp::Assign) { + if (auto* lid = dynamic_cast(bin->lhs)) { + bool found = false; + for (auto it = localsStack.rbegin(); it != localsStack.rend(); ++it) { + if (it->find(lid->name) != it->end()) { found = true; break; } + } + if (!found) result.addError("Assignment to undeclared variable '" + lid->name + "'"); + } + } + return; + } + if (auto* un = dynamic_cast(e)) { + analyzeExpr(un->rhs); + return; + } + if (auto* call = dynamic_cast(e)) { + if (auto* id = dynamic_cast(call->callee)) { + if (id->name == "output" || id->name == "print") { + for (auto*& a : call->args) analyzeExpr(a); + return; + } + bool found = false; + for (auto* m : curClass->methods) if (m->name == id->name) { found = true; break; } + if (!found) result.addError("Call to undeclared method '" + id->name + "'"); + } else { + analyzeExpr(call->callee); + } + for (auto*& a : call->args) analyzeExpr(a); + return; + } + if (auto* ma = dynamic_cast(e)) { + analyzeExpr(ma->object); + return; + } + if (auto* idx = dynamic_cast(e)) { + analyzeExpr(idx->base); + analyzeExpr(idx->index); + return; + } + if (auto* id = dynamic_cast(e)) { + bool found = false; + for (auto it = localsStack.rbegin(); it != localsStack.rend(); ++it) { + if (it->find(id->name) != it->end()) { found = true; break; } + } + if (found) { + usedLocals.push_back(id->name); + } else { + result.addError("Use of undeclared identifier '" + id->name + "'"); + } + return; + } +} + +bool SemanticAnalyzer::isLiteral(AST::Expr* e) { + return dynamic_cast(e) || dynamic_cast(e) || dynamic_cast(e); +} + +bool SemanticAnalyzer::foldConstantsInExpr(AST::Expr*& e) { + if (!e) return false; + if (auto* bin = dynamic_cast(e)) { + bool l = foldConstantsInExpr(bin->lhs); + bool r = foldConstantsInExpr(bin->rhs); + if (auto* L = dynamic_cast(bin->lhs)) { + if (auto* R = dynamic_cast(bin->rhs)) { + long long a = L->value; + long long b = R->value; + if (bin->op == AST::BinOp::Add || bin->op == AST::BinOp::Sub || bin->op == AST::BinOp::Mul || bin->op == AST::BinOp::Div) { + long long resv = 0; + switch (bin->op) { + case AST::BinOp::Add: resv = a + b; break; + case AST::BinOp::Sub: resv = a - b; break; + case AST::BinOp::Mul: resv = a * b; break; + case AST::BinOp::Div: + if (b == 0) { result.addError("Division by zero in constant expression"); return false; } + resv = a / b; break; + default: break; + } + delete e; + e = new AST::IntLiteral(resv); + return true; + } + if (bin->op == AST::BinOp::Gt || bin->op == AST::BinOp::Lt) { + bool rr = false; + if (bin->op == AST::BinOp::Gt) rr = (a > b); + else rr = (a < b); + delete e; + e = new AST::BoolLiteral(rr); + return true; + } + if (bin->op == AST::BinOp::Eq) { + bool rr = (a == b); + delete e; + e = new AST::BoolLiteral(rr); + return true; + } + } + } + if (auto* LB = dynamic_cast(bin->lhs)) { + if (auto* RB = dynamic_cast(bin->rhs)) { + if (bin->op == AST::BinOp::Eq) { + bool rr = (LB->value == RB->value); + delete e; + e = new AST::BoolLiteral(rr); + return true; + } + } + } + return l || r; + } + if (auto* un = dynamic_cast(e)) { + bool r = foldConstantsInExpr(un->rhs); + if (auto* R = dynamic_cast(un->rhs)) { + if (un->op == AST::Unary::Op::Neg) { + long long val = -R->value; + delete e; + e = new AST::IntLiteral(val); + return true; + } + } + return r; + } + if (auto* call = dynamic_cast(e)) { + bool changed = false; + changed |= foldConstantsInExpr(call->callee); + for (auto*& a : call->args) changed |= foldConstantsInExpr(a); + return changed; + } + if (auto* ma = dynamic_cast(e)) { + bool changed = foldConstantsInExpr(ma->object); + return changed; + } + if (auto* idx = dynamic_cast(e)) { + bool b = foldConstantsInExpr(idx->base); + bool i = foldConstantsInExpr(idx->index); + return b || i; + } + return false; +} + +void SemanticAnalyzer::simplifyIf(AST::Stmt*& s) { + if (!s) return; + if (auto* ifs = dynamic_cast(s)) { + if (auto* bl = dynamic_cast(ifs->cond)) { + if (bl->value) { + if (ifs->elseS) { + result.optimizations.push_back("Removed else-branch because condition is always true"); + } else { + result.optimizations.push_back("Simplified 'if true' condition"); + } + AST::Stmt* newstmt = nullptr; + if (auto* tb = dynamic_cast(ifs->thenS)) { + newstmt = tb; + ifs->thenS = nullptr; + ifs->elseS = nullptr; + delete ifs->cond; + s = newstmt; + } else { + newstmt = ifs->thenS; + ifs->thenS = nullptr; + ifs->elseS = nullptr; + delete ifs; + s = newstmt; + } + } else { + if (ifs->thenS) { + result.optimizations.push_back("Removed then-branch because condition is always false"); + } + if (ifs->elseS) { + AST::Stmt* newstmt = ifs->elseS; + ifs->elseS = nullptr; + ifs->thenS = nullptr; + delete ifs; + s = newstmt; + } else { + delete ifs; + s = new AST::Block(); + } + } + } + } else if (auto* blk = dynamic_cast(s)) { + for (auto*& st : blk->stmts) { + simplifyIf(st); + } + } +} + +void SemanticAnalyzer::removeUnreachableInBlock(AST::Block* b) { + if (!b) return; + std::vector newstmts; + bool seenReturn = false; + bool removedAny = false; + for (auto* s : b->stmts) { + if (seenReturn) { + delete s; + removedAny = true; + continue; + } + newstmts.push_back(s); + if (dynamic_cast(s)) seenReturn = true; + } + if (removedAny) { + result.optimizations.push_back("Removed unreachable statements after return"); + } + b->stmts = std::move(newstmts); +} diff --git a/semantic.hpp b/semantic.hpp new file mode 100644 index 0000000..22cfef0 --- /dev/null +++ b/semantic.hpp @@ -0,0 +1,52 @@ +#pragma once +#include +#include +#include +#include "ast.hpp" + +struct SemanticResult { + bool ok; + std::vector errors; + std::vector warnings; + std::vector optimizations; + void addError(const std::string& s) { errors.push_back(s); ok = false; } + SemanticResult() : ok(true) {} +}; + +class SemanticAnalyzer { +public: + SemanticAnalyzer(); + SemanticResult analyze(AST::Program* p); + +private: + std::unordered_map classes; + + AST::ClassDecl* curClass = nullptr; + AST::MethodDecl* curMethod = nullptr; + + std::vector> localsStack; + + SemanticResult result; + + std::vector declaredLocals; + std::vector usedLocals; + + void indexClasses(AST::Program* p); + void analyzeClass(AST::ClassDecl* c); + void analyzeMethod(AST::MethodDecl* m); + void analyzeBlock(AST::Block* b); + void analyzeStmt(AST::Stmt*& s); + void analyzeExpr(AST::Expr*& e); + + bool foldConstantsInExpr(AST::Expr*& e); + void simplifyIf(AST::Stmt*& s); + void removeUnreachableInBlock(AST::Block* b); + + std::string typeOfExpr(AST::Expr* e); + bool isLiteral(AST::Expr* e); + void pushScope(); + void popScope(); + void declareLocal(const std::string& name, const std::string& type); + + std::string curPos(AST::Node* n); +}; diff --git a/tests/old/test.o b/tests/old/test.o index d1a85d7..1043f59 100644 --- a/tests/old/test.o +++ b/tests/old/test.o @@ -17,6 +17,6 @@ end class Main is method main() : Void => var d : Dog; - d.Foo(0); # для примера, так как вызовы методов надо упрощать + d.Foo(0); d.bark() end diff --git a/tests/old/test11.o b/tests/old/test11.o index 0873c64..53674d7 100644 --- a/tests/old/test11.o +++ b/tests/old/test11.o @@ -12,6 +12,6 @@ class Main is var a : Animal; var d : Dog; method main() : Void => - a := d; # полиморфизм - a.speak() # должен вызвать Dog.speak() + a := d; + a.speak() end \ No newline at end of file diff --git a/tests/test.o b/tests/test.o new file mode 100644 index 0000000..452c0a8 --- /dev/null +++ b/tests/test.o @@ -0,0 +1,21 @@ +class Types is + var i : Int; + var j : Int; + var s : String; + var b : Bool; +end + +class Main is + var flag : Bool; + method main() : Void => + flag := false; + if flag == false then + output("keywords"); + output(1 + 2 * 3); + output((10 - 5) / 5); + output(true); + output("done") + else + output("never") + end +end diff --git a/tests/test1.o b/tests/test1.o index 3c26240..3c25b0d 100644 --- a/tests/test1.o +++ b/tests/test1.o @@ -1,4 +1,9 @@ -class Point is - var x : Int; - var y : Int; +class Main is + var i : Int; + method main() : Void => + i := 0; + while i < 3 do + output(i); + i := i + 1 + end end diff --git a/tests/test2.o b/tests/test2.o index e906a11..7843c9b 100644 --- a/tests/test2.o +++ b/tests/test2.o @@ -1,4 +1,9 @@ -class Math is - method Add(a : Int, b : Int) : Int => a + b - method Neg(v : Float) : Float => -v +class Main is + var i : Int; + method main() : Void => + if true then + output("true"); + else + output("false"); + end end diff --git a/tests/test3.o b/tests/test3.o index d1cdeed..7f1d4ad 100644 --- a/tests/test3.o +++ b/tests/test3.o @@ -1,7 +1,14 @@ -class Main is - method Run() : Int => - if true then - return 42 - else - return 0 -end +class Main is + var x : Int; + method main() : Void => + x := 5; + if x < 0 then + output("neg"); + else if x == 0 then + output("zero"); + else if x < 10 then + output("small"); + else + output("big") + end +end diff --git a/tests/test4.o b/tests/test4.o new file mode 100644 index 0000000..66dd971 --- /dev/null +++ b/tests/test4.o @@ -0,0 +1,11 @@ +class Main is + method main() : Int => + if true then + output("then-part"); + return 0; + output("unreachable"); + output("still unreachable") + else + output("else-part") + end +end diff --git a/tests/test5.o b/tests/test5.o new file mode 100644 index 0000000..a8e86bd --- /dev/null +++ b/tests/test5.o @@ -0,0 +1,7 @@ +class Main is + method main() : Int => + output("before"); + return 1; + output("after"); + output("after2") +end diff --git a/tokens.hpp b/tokens.hpp index 5571070..5ed661d 100644 --- a/tokens.hpp +++ b/tokens.hpp @@ -7,20 +7,22 @@ enum class TokenKind { CLASS, VAR, IS, END, - METHOD, RETURN, IF, THEN, ELSE, + METHOD, RETURN, IF, THEN, ELSE, ELSEIF, + WHILE, DO, TRUEKW, FALSEKW, - IDENTIFIER, TYPE_NAME, INT_LITERAL, + IDENTIFIER, TYPE_NAME, INT_LITERAL, STRING_LITERAL, COLON, SEMICOLON, COMMA, - LPAREN, RPAREN, LBRACE, RBRACE, + LPAREN, RPAREN, LBRACE, RBRACE, LBRACKET, RBRACKET, ASSIGN, ARROW, PLUS, MINUS, STAR, SLASH, + DOT, GT, LT, EQEQ, EQUAL, END_OF_FILE }; struct Token { - TokenKind kind; + TokenKind kind; std::string lexeme; - int line; - int column; + int line; + int column; Token(TokenKind k, std::string lx, int ln, int col) : kind(k), lexeme(std::move(lx)), line(ln), column(col) {} virtual ~Token() = default; @@ -47,6 +49,12 @@ struct IntegerToken : Token { : Token(TokenKind::INT_LITERAL, lx, ln, col), value(v) {} }; +struct StringToken : Token { + std::string value; + StringToken(const std::string& lx, std::string v, int ln, int col) + : Token(TokenKind::STRING_LITERAL, lx, ln, col), value(std::move(v)) {} +}; + struct SymbolToken : Token { SymbolToken(TokenKind k, const std::string& lx, int ln, int col) : Token(k, lx, ln, col) {} @@ -63,11 +71,15 @@ inline const char* TokenKindToString(TokenKind k) { case TokenKind::IF: return "IF"; case TokenKind::THEN: return "THEN"; case TokenKind::ELSE: return "ELSE"; + case TokenKind::ELSEIF: return "ELSEIF"; + case TokenKind::WHILE: return "WHILE"; + case TokenKind::DO: return "DO"; case TokenKind::TRUEKW: return "TRUE"; case TokenKind::FALSEKW: return "FALSE"; case TokenKind::IDENTIFIER: return "IDENTIFIER"; case TokenKind::TYPE_NAME: return "TYPE_NAME"; case TokenKind::INT_LITERAL: return "INT_LITERAL"; + case TokenKind::STRING_LITERAL: return "STRING_LITERAL"; case TokenKind::COLON: return "COLON"; case TokenKind::SEMICOLON: return "SEMICOLON"; case TokenKind::COMMA: return "COMMA"; @@ -75,12 +87,19 @@ inline const char* TokenKindToString(TokenKind k) { case TokenKind::RPAREN: return "RPAREN"; case TokenKind::LBRACE: return "LBRACE"; case TokenKind::RBRACE: return "RBRACE"; + case TokenKind::LBRACKET: return "LBRACKET"; + case TokenKind::RBRACKET: return "RBRACKET"; case TokenKind::ASSIGN: return "ASSIGN"; case TokenKind::ARROW: return "ARROW"; case TokenKind::PLUS: return "PLUS"; case TokenKind::MINUS: return "MINUS"; case TokenKind::STAR: return "STAR"; case TokenKind::SLASH: return "SLASH"; + case TokenKind::DOT: return "DOT"; + case TokenKind::GT: return "GT"; + case TokenKind::LT: return "LT"; + case TokenKind::EQEQ: return "EQEQ"; + case TokenKind::EQUAL: return "EQUAL"; case TokenKind::END_OF_FILE: return "EOF"; } return "UNKNOWN"; @@ -95,6 +114,8 @@ inline void EmitToken(std::unique_ptr t) { } else if (t->kind == TokenKind::INT_LITERAL) { auto* it = static_cast(t.get()); std::cout << "(" << it->value << ")"; + } else if (t->kind == TokenKind::STRING_LITERAL) { + std::cout << "(" << t->lexeme << ")"; } std::cout << "\n"; g_tokens.emplace_back(std::move(t));