From c325b671c1a44c90c8ce570b901219bcc2ae0a38 Mon Sep 17 00:00:00 2001 From: "John Hodge (bugs)" Date: Sun, 7 Dec 2014 09:46:00 +0800 Subject: Macro evaluation hacked in (... not quite in yet, but framework is there) Macro definitions not implemented, evil hack to define try! is present --- ast/ast.cpp | 8 ++- ast/ast.hpp | 17 +++++- macros.cpp | 105 +++++++++++++++++++++++++++++++++ macros.hpp | 69 ++++++++++++++++++++++ parse/expr.cpp | 162 ++++++++++++++++++++++++++++++++++++++++++++++++--- parse/lex.cpp | 50 +++++++++++++++- parse/lex.hpp | 42 ++++++++++--- parse/parseerror.cpp | 12 ++++ parse/parseerror.hpp | 18 ++++++ parse/preproc.cpp | 20 +------ parse/preproc.hpp | 6 +- parse/root.cpp | 41 ++++++++++++- parse/tokentree.hpp | 10 ++++ 13 files changed, 516 insertions(+), 44 deletions(-) create mode 100644 macros.cpp create mode 100644 macros.hpp create mode 100644 parse/tokentree.hpp diff --git a/ast/ast.cpp b/ast/ast.cpp index 1683bf91..a6eaa039 100644 --- a/ast/ast.cpp +++ b/ast/ast.cpp @@ -39,7 +39,7 @@ Pattern::Pattern(TagEnumVariant, Path path, ::std::vector sub_patterns) } -Function::Function(::std::string name, TypeParams params, TypeRef ret_type, ::std::vector args, Expr code) +Function::Function(::std::string name, TypeParams params, Class fcn_class, TypeRef ret_type, ::std::vector args, Expr code) { } @@ -76,9 +76,15 @@ ExprNode::ExprNode() ExprNode::ExprNode(TagBlock, ::std::vector nodes) { } +ExprNode::ExprNode(TagLetBinding, Pattern pat, ExprNode value) +{ +} ExprNode::ExprNode(TagInteger, uint64_t value, enum eCoreType datatype) { } +ExprNode::ExprNode(TagStructLiteral, Path path, ExprNode base_value, ::std::vector< ::std::pair< ::std::string,ExprNode> > values ) +{ +} ExprNode::ExprNode(TagCallPath, Path path, ::std::vector args) { } diff --git a/ast/ast.hpp b/ast/ast.hpp index ab5f73f5..2c8b06c0 100644 --- a/ast/ast.hpp +++ b/ast/ast.hpp @@ -69,12 +69,18 @@ public: struct TagBlock {}; ExprNode(TagBlock, ::std::vector nodes); + struct TagLetBinding {}; + ExprNode(TagLetBinding, Pattern pat, ExprNode value); + struct TagAssign {}; ExprNode(TagAssign, ExprNode slot, ExprNode value) {} struct TagInteger {}; ExprNode(TagInteger, uint64_t value, enum eCoreType datatype); + struct TagStructLiteral {}; + ExprNode(TagStructLiteral, Path path, ExprNode base_value, ::std::vector< ::std::pair< ::std::string,ExprNode> > values ); + struct TagCallPath {}; ExprNode(TagCallPath, Path path, ::std::vector args); @@ -95,7 +101,16 @@ public: class Function { public: - Function(::std::string name, TypeParams params, TypeRef ret_type, ::std::vector args, Expr code); + + enum Class + { + CLASS_UNBOUND, + CLASS_REFMETHOD, + CLASS_MUTMETHOD, + CLASS_VALMETHOD, + }; + + Function(::std::string name, TypeParams params, Class fcn_class, TypeRef ret_type, ::std::vector args, Expr code); }; class Impl diff --git a/macros.cpp b/macros.cpp new file mode 100644 index 00000000..3561a911 --- /dev/null +++ b/macros.cpp @@ -0,0 +1,105 @@ +/* + */ +#include "macros.hpp" +#include "parse/parseerror.hpp" +#include "parse/tokentree.hpp" + +#define FOREACH(basetype, it, src) for(basetype::const_iterator it = src.begin(); it != src.end(); ++ it) + +typedef ::std::map< ::std::string, MacroRules> t_macro_regs; + +t_macro_regs g_macro_registrations; + +void Macro_InitDefaults() +{ + // try!() macro + { + MacroRule rule; + rule.m_pattern.push_back( MacroPatEnt("val", MacroPatEnt::PAT_EXPR) ); + // match $rule { + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_RWORD_MATCH)) ); + rule.m_contents.push_back( MacroRuleEnt("val") ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_BRACE_OPEN)) ); + // Ok(v) => v, + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_IDENT, "Ok")) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_PAREN_OPEN)) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_IDENT, "v")) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_PAREN_CLOSE)) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_FATARROW)) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_IDENT, "v")) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_COMMA)) ); + // Err(e) => return Err(r), + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_IDENT, "Err")) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_PAREN_OPEN)) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_IDENT, "e")) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_PAREN_CLOSE)) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_FATARROW)) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_RWORD_RETURN)) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_IDENT, "Err")) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_PAREN_OPEN)) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_IDENT, "e")) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_PAREN_CLOSE)) ); + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_COMMA)) ); + // } + rule.m_contents.push_back( MacroRuleEnt(Token(TOK_BRACE_CLOSE)) ); + MacroRules rules; + rules.push_back(rule); + g_macro_registrations.insert( make_pair(::std::string("try"), rules)); + } +} + +MacroExpander Macro_Invoke(const char* name, TokenTree input) +{ + // XXX: EVIL HACK! - This should be removed when std loading is implemented + if( g_macro_registrations.size() == 0 ) { + Macro_InitDefaults(); + } + // 1. Locate macro with that name + t_macro_regs::iterator macro_reg = g_macro_registrations.find(name); + if( macro_reg != g_macro_registrations.end() ) + { + const MacroRules& rules = macro_reg->second; + // 2. Check input token tree against possible variants + // 3. Bind names + // 4. Return expander + FOREACH(MacroRules, rule_it, rules) + { + // Create token stream for input tree + TTStream lex(input); + ::std::map bound_tts; + // Parse according to rules + bool fail = false; + FOREACH(::std::vector, pat_it, rule_it->m_pattern) + { + const MacroPatEnt& pat = *pat_it; + switch(pat.type) + { + case MacroPatEnt::PAT_TOKEN: + if( lex.getToken().type() != pat.tok.type() ) { + fail = true; + break; + } + break; + case MacroPatEnt::PAT_EXPR: + bound_tts.insert( std::make_pair(pat.name.c_str(), Parse_TT_Expr(lex)) ); + break; + default: + throw ParseError::Todo("macro pattern matching"); + } + if( fail ) + break; + } + if( !fail ) { + throw ParseError::Todo("Macro expansions"); + } + } + throw ParseError::Todo("Error when macro fails to match"); + } + + throw ParseError::Generic( ::std::string("Macro '") + name + "' was not found" ); +} + +Token MacroExpander::realGetToken() +{ + throw ParseError::Todo("MacroExpander"); +} diff --git a/macros.hpp b/macros.hpp new file mode 100644 index 00000000..5c86e22c --- /dev/null +++ b/macros.hpp @@ -0,0 +1,69 @@ +#ifndef MACROS_HPP_INCLUDED +#define MACROS_HPP_INCLUDED + +#include "parse/lex.hpp" +#include + +class MacroRuleEnt +{ + Token tok; + ::std::string name; +public: + MacroRuleEnt(Token tok): + tok(tok), + name("") + { + } + MacroRuleEnt(::std::string name): + name(name) + { + } +}; +struct MacroPatEnt +{ + Token tok; + ::std::string name; + enum Type { + PAT_TOKEN, + PAT_TT, + PAT_IDENT, + PAT_PATH, + PAT_EXPR, + PAT_STMT, + PAT_BLOCK, + } type; + + MacroPatEnt(::std::string name, Type type): + tok(), + name(name), + type(type) + { + } +}; + +/// A rule within a macro_rules! blcok +class MacroRule +{ + struct MacroRuleState + { + ::std::map< ::std::string, TokenTree> m_mappings; + size_t ofs; + }; +public: + ::std::vector m_pattern; + ::std::vector m_contents; +}; + +/// A sigle 'macro_rules!' block +typedef ::std::vector MacroRules; + +class MacroExpander: + public TokenStream +{ +public: + virtual Token realGetToken(); +}; + +extern MacroExpander Macro_Invoke(const char* name, TokenTree input); + +#endif // MACROS_HPP_INCLUDED diff --git a/parse/expr.cpp b/parse/expr.cpp index 90a39b04..cb3b80e7 100644 --- a/parse/expr.cpp +++ b/parse/expr.cpp @@ -4,7 +4,9 @@ #include "parseerror.hpp" #include "../ast/ast.hpp" #include "common.hpp" +#include "../macros.hpp" #include +#include "tokentree.hpp" using AST::ExprNode; @@ -82,6 +84,7 @@ AST::Pattern Parse_Pattern(TokenStream& lex) default: throw ParseError::Unexpected(tok); } + throw ParseError::BugCheck("Parse_TT_Stmt should early return"); } ExprNode Parse_ExprBlockNode(TokenStream& lex) @@ -102,12 +105,15 @@ ExprNode Parse_ExprBlockNode(TokenStream& lex) nodes.push_back(Parse_ExprBlockNode(lex)); trailing_value = true; break; - case TOK_RWORD_LET: + case TOK_RWORD_LET: { //ret.append(); - throw ParseError::Todo("block let"); + AST::Pattern pat = Parse_Pattern(lex); + GET_CHECK_TOK(tok, lex, TOK_EQUAL); + AST::ExprNode val = Parse_Expr1(lex); GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); trailing_value = false; - break; + nodes.push_back( ExprNode(ExprNode::TagLetBinding(), pat, val)); + break; } case TOK_RWORD_LOOP: throw ParseError::Todo("loop"); break; @@ -360,10 +366,31 @@ AST::ExprNode Parse_ExprVal(TokenStream& lex) path = Parse_Path(lex, true, PATH_GENERIC_EXPR); switch( GET_TOK(tok, lex) ) { - case TOK_BRACE_OPEN: - // Structure literal - throw ParseError::Todo("Structure literal"); - break; + case TOK_BRACE_OPEN: { + // Braced structure literal + // - A series of 0 or more pairs of : , + // - '..' + ::std::vector< ::std::pair< ::std::string, AST::ExprNode> > items; + while( GET_TOK(tok, lex) == TOK_IDENT ) + { + ::std::string name = tok.str(); + GET_CHECK_TOK(tok, lex, TOK_COLON); + AST::ExprNode val = Parse_Expr0(lex); + items.push_back( ::std::make_pair(name, val) ); + if( GET_TOK(tok,lex) == TOK_BRACE_CLOSE ) + break; + CHECK_TOK(tok, TOK_COMMA); + } + AST::ExprNode base_val; + if( tok.type() == TOK_DOUBLE_DOT ) + { + // default + base_val = Parse_Expr0(lex); + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_BRACE_CLOSE); + return ExprNode(ExprNode::TagStructLiteral(), path, base_val, items); + } case TOK_PAREN_OPEN: { lex.putback(tok); // Function call @@ -379,7 +406,128 @@ AST::ExprNode Parse_ExprVal(TokenStream& lex) return ExprNode(ExprNode::TagInteger(), tok.intval(), tok.datatype()); case TOK_FLOAT: throw ParseError::Todo("Float"); + case TOK_MACRO: { + // Need to create a token tree, pass to the macro, then pass the result of that to Parse_Expr0 + MacroExpander expanded_macro = Macro_Invoke(tok.str().c_str(), Parse_TT(lex)); + + return Parse_Expr0(expanded_macro); + } default: throw ParseError::Unexpected(tok); } } + +// Token Tree Parsing +TokenTree Parse_TT(TokenStream& lex) +{ + Token tok = lex.getToken(); + eTokenType closer = TOK_PAREN_CLOSE; + switch(tok.type()) + { + case TOK_PAREN_OPEN: + closer = TOK_PAREN_CLOSE; + break; + case TOK_SQUARE_OPEN: + closer = TOK_SQUARE_CLOSE; + break; + case TOK_BRACE_OPEN: + closer = TOK_BRACE_CLOSE; + break; + default: + return TokenTree(tok); + } + + ::std::vector items; + items.push_back(tok); + while(GET_TOK(tok, lex) != closer && tok.type() != TOK_EOF) + { + lex.putback(tok); + items.push_back(Parse_TT(lex)); + } + items.push_back(tok); + return TokenTree(items); +} + +TokenTree Parse_TT_Path(TokenStream& lex) +{ + throw ParseError::Todo("TokenTree path"); +} +TokenTree Parse_TT_Val(TokenStream& lex) +{ + Token tok; + ::std::vector ret; + switch(GET_TOK(tok, lex)) + { + case TOK_PAREN_OPEN: + lex.putback(tok); + return Parse_TT(lex); + + case TOK_IDENT: + case TOK_DOUBLE_COLON: { + lex.putback(tok); + TokenTree inner = Parse_TT_Path(lex); + if(GET_TOK(tok, lex) == TOK_BRACE_OPEN) { + lex.putback(tok); + ret.push_back(inner); + ret.push_back(Parse_TT(lex)); + } + else { + lex.putback(tok); + return inner; + } + break; } + case TOK_RWORD_MATCH: + ret.push_back(TokenTree(tok)); + ret.push_back(Parse_TT(lex)); + break; + case TOK_RWORD_IF: + ret.push_back(TokenTree(tok)); + ret.push_back(Parse_TT(lex)); + if( GET_TOK(tok, lex) == TOK_RWORD_ELSE ) { + ret.push_back(TokenTree(tok)); + ret.push_back(Parse_TT(lex)); + } + else { + lex.putback(tok); + } + break; + default: + // Oh, fail :( + break; + } + return TokenTree(ret); +} +TokenTree Parse_TT_Expr(TokenStream& lex) +{ + Token tok; + ::std::vector ret; + + ret.push_back(Parse_TT_Val(lex)); + // 1. Get left associative blocks until nothing matches + bool cont = true; + while(cont) + { + switch(GET_TOK(tok, lex)) + { + case TOK_PLUS: + case TOK_DASH: + ret.push_back(tok); + ret.push_back(Parse_TT_Val(lex)); + break; + default: + lex.putback(tok); + cont = false; + break; + } + } + return TokenTree(ret); + +} +TokenTree Parse_TT_Stmt(TokenStream& lex) +{ + throw ParseError::Todo("Parse_TT_Stmt"); +} +TokenTree Parse_TT_Block(TokenStream& lex) +{ + throw ParseError::Todo("Parse_TT_Block"); +} diff --git a/parse/lex.cpp b/parse/lex.cpp index 99568a11..99d787fb 100644 --- a/parse/lex.cpp +++ b/parse/lex.cpp @@ -133,9 +133,9 @@ static const struct { TOKENT("pure", TOK_RWORD_PURE), TOKENT("ref", TOK_RWORD_REF), TOKENT("return", TOK_RWORD_RETURN), + TOKENT("self", TOK_RWORD_SELF), TOKENT("sizeof", TOK_RWORD_SIZEOF), TOKENT("static", TOK_RWORD_STATIC), - TOKENT("self", TOK_RWORD_SELF), TOKENT("struct", TOK_RWORD_STRUCT), TOKENT("super", TOK_RWORD_SUPER), TOKENT("true", TOK_RWORD_TRUE), @@ -416,7 +416,6 @@ const char* Token::typestr(enum eTokenType type) case TOK_INTEGER: return "TOK_INTEGER"; case TOK_CHAR: return "TOK_CHAR"; case TOK_FLOAT: return "TOK_FLOAT"; - case TOK_UNDERSCORE: return "TOK_UNDERSCORE"; case TOK_CATTR_OPEN: return "TOK_CATTR_OPEN"; case TOK_ATTR_OPEN: return "TOK_ATTR_OPEN"; @@ -542,3 +541,50 @@ const char* Token::typestr(enum eTokenType type) return os; } +TokenTree::TokenTree(Token tok) +{ + +} +TokenTree::TokenTree(::std::vector subtrees) +{ + +} + +TTStream::TTStream(const TokenTree& input_tt): + m_input_tt(input_tt), + m_cur_layer(&input_tt) +{ +} +TTStream::~TTStream() +{ +} +Token TTStream::realGetToken() +{ + return Token(TOK_EOF); +} + +TokenStream::TokenStream(): + m_cache_valid(false) +{ +} +TokenStream::~TokenStream() +{ +} + +Token TokenStream::getToken() +{ + if( m_cache_valid ) + { + m_cache_valid = false; + return m_cache; + } + else + { + return this->realGetToken(); + } +} +void TokenStream::putback(Token tok) +{ + m_cache_valid = true; + m_cache = tok; +} diff --git a/parse/lex.hpp b/parse/lex.hpp index a219ae94..88aef072 100644 --- a/parse/lex.hpp +++ b/parse/lex.hpp @@ -20,7 +20,6 @@ enum eTokenType TOK_INTEGER, TOK_CHAR, TOK_FLOAT, - TOK_UNDERSCORE, TOK_CATTR_OPEN, TOK_ATTR_OPEN, @@ -154,11 +153,11 @@ public: Token(uint64_t val, enum eCoreType datatype); Token(double val, enum eCoreType datatype); - enum eTokenType type() { return m_type; } - const ::std::string& str() { return m_str; } - enum eCoreType datatype() { return m_datatype; } - uint64_t intval() { return m_intval; } - double floatval() { return m_floatval; } + enum eTokenType type() const { return m_type; } + const ::std::string& str() const { return m_str; } + enum eCoreType datatype() const { return m_datatype; } + uint64_t intval() const { return m_intval; } + double floatval() const { return m_floatval; } static const char* typestr(enum eTokenType type); }; @@ -167,9 +166,15 @@ extern ::std::ostream& operator<<(::std::ostream& os, Token& tok); class TokenStream { + bool m_cache_valid; + Token m_cache; public: - virtual Token getToken() = 0; - virtual void putback(Token tok) = 0; + TokenStream(); + virtual ~TokenStream(); + Token getToken(); + void putback(Token tok); +protected: + virtual Token realGetToken() = 0; }; class Lexer @@ -192,4 +197,25 @@ private: class EndOfFile {}; }; +class TokenTree +{ +public: + TokenTree(Token tok); + TokenTree(::std::vector subtrees); +}; + +class TTStream: + public TokenStream +{ + const TokenTree& m_input_tt; + const TokenTree* m_cur_layer; + ::std::vector m_index_stack; +public: + TTStream(const TokenTree& input_tt); + ~TTStream(); + +protected: + virtual Token realGetToken(); +}; + #endif // LEX_HPP_INCLUDED diff --git a/parse/parseerror.cpp b/parse/parseerror.cpp index 044847e7..37beb863 100644 --- a/parse/parseerror.cpp +++ b/parse/parseerror.cpp @@ -7,6 +7,18 @@ ParseError::Base::~Base() throw() { } +ParseError::Generic::Generic(::std::string message): + m_message(message) +{ + ::std::cout << "Generic(" << message << ")" << ::std::endl; +} + +ParseError::BugCheck::BugCheck(::std::string message): + m_message(message) +{ + ::std::cout << "BugCheck(" << message << ")" << ::std::endl; +} + ParseError::Todo::Todo(::std::string message): m_message(message) { diff --git a/parse/parseerror.hpp b/parse/parseerror.hpp index b708565d..11324476 100644 --- a/parse/parseerror.hpp +++ b/parse/parseerror.hpp @@ -13,6 +13,24 @@ public: virtual ~Base() throw(); }; +class Generic: + public Base +{ + ::std::string m_message; +public: + Generic(::std::string message); + virtual ~Generic() throw () {} +}; + +class BugCheck: + public Base +{ + ::std::string m_message; +public: + BugCheck(::std::string message); + virtual ~BugCheck() throw () {} +}; + class Todo: public Base { diff --git a/parse/preproc.cpp b/parse/preproc.cpp index fa051a3e..2287c41e 100644 --- a/parse/preproc.cpp +++ b/parse/preproc.cpp @@ -2,8 +2,7 @@ #include Preproc::Preproc(::std::string path): - m_lex(path), - m_cache_valid(false) + m_lex(path) { //ctor } @@ -31,20 +30,7 @@ Token Preproc::getTokenInt() } } -Token Preproc::getToken() +Token Preproc::realGetToken() { - if( m_cache_valid ) - { - m_cache_valid = false; - return m_cache; - } - else - { - return this->getTokenInt(); - } -} -void Preproc::putback(Token tok) -{ - m_cache_valid = true; - m_cache = tok; + return getTokenInt(); } diff --git a/parse/preproc.hpp b/parse/preproc.hpp index 827ce249..bedb3076 100644 --- a/parse/preproc.hpp +++ b/parse/preproc.hpp @@ -8,15 +8,11 @@ class Preproc: { Lexer m_lex; - bool m_cache_valid; - Token m_cache; - public: Preproc(::std::string path); ~Preproc(); - virtual Token getToken(); - virtual void putback(Token tok); + virtual Token realGetToken(); private: Token getTokenInt(); }; diff --git a/parse/root.cpp b/parse/root.cpp index c82527a2..2dac321a 100644 --- a/parse/root.cpp +++ b/parse/root.cpp @@ -232,6 +232,8 @@ void Parse_TypeConds(TokenStream& lex, AST::TypeParams& params) /// Parse a function definition (after the 'fn') AST::Function Parse_FunctionDef(TokenStream& lex) { + TRACE_FUNCTION; + Token tok; // Name @@ -254,12 +256,45 @@ AST::Function Parse_FunctionDef(TokenStream& lex) else { lex.putback(tok); } + + AST::Function::Class fcn_class = AST::Function::CLASS_UNBOUND; GET_CHECK_TOK(tok, lex, TOK_PAREN_OPEN); + GET_TOK(tok, lex); + if( tok.type() == TOK_AMP ) + { + // By-reference method + if( GET_TOK(tok, lex) == TOK_LIFETIME ) + { + throw ParseError::Todo("Lifetimes on self in methods"); + } + if( tok.type() == TOK_RWORD_MUT ) + { + GET_CHECK_TOK(tok, lex, TOK_RWORD_SELF); + fcn_class = AST::Function::CLASS_MUTMETHOD; + } + else + { + CHECK_TOK(tok, TOK_RWORD_SELF); + fcn_class = AST::Function::CLASS_REFMETHOD; + } + GET_TOK(tok, lex); + } + else if( tok.type() == TOK_RWORD_SELF ) + { + // By-value method + fcn_class = AST::Function::CLASS_VALMETHOD; + GET_TOK(tok, lex); + throw ParseError::Todo("By-value methods"); + } + else + { + // Unbound method + } ::std::vector args; - if( GET_TOK(tok, lex) != TOK_PAREN_CLOSE ) + if( tok.type() != TOK_PAREN_CLOSE ) { - // Argument list lex.putback(tok); + // Argument list do { GET_CHECK_TOK(tok, lex, TOK_IDENT); ::std::string name = tok.str(); @@ -287,7 +322,7 @@ AST::Function Parse_FunctionDef(TokenStream& lex) AST::Expr code = Parse_ExprBlock(lex); - return AST::Function(name, params, ret_type, args, code); + return AST::Function(name, params, fcn_class, ret_type, args, code); } AST::Module Parse_ModRoot(bool is_own_file, Preproc& lex) diff --git a/parse/tokentree.hpp b/parse/tokentree.hpp new file mode 100644 index 00000000..52871cce --- /dev/null +++ b/parse/tokentree.hpp @@ -0,0 +1,10 @@ +#ifndef TOKENTREE_HPP_INCLUDED +#define TOKENTREE_HPP_INCLUDED + +#include "lex.hpp" + + +extern TokenTree Parse_TT(TokenStream& lex); +extern TokenTree Parse_TT_Expr(TokenStream& lex); + +#endif // TOKENTREE_HPP_INCLUDED -- cgit v1.2.3