├── .gitignore ├── Grammar.txt ├── Makefile ├── README.md ├── hap └── main.cpp ├── lib ├── Atomic.cpp ├── Atomic.h ├── Context.cpp ├── Context.h ├── Environment.cpp ├── Environment.h ├── Expression │ ├── BinaryExpression.cpp │ ├── BinaryExpression.h │ ├── CallExpression.cpp │ ├── CallExpression.h │ ├── DotExpression.cpp │ ├── DotExpression.h │ ├── Expression.cpp │ ├── Expression.h │ ├── IdentifierExpression.cpp │ ├── IdentifierExpression.h │ ├── ListExpression.cpp │ ├── ListExpression.h │ ├── MapExpression.cpp │ ├── MapExpression.h │ ├── SubscriptExpression.cpp │ ├── SubscriptExpression.h │ ├── UnaryExpression.cpp │ └── UnaryExpression.h ├── Interpreter.cpp ├── Interpreter.h ├── Operator.cpp ├── Operator.h ├── Parser │ ├── Parser.cpp │ ├── Parser.h │ ├── core.cpp │ ├── expression.cpp │ ├── statement.cpp │ └── value.cpp ├── Statement │ ├── AtomicStatement.cpp │ ├── AtomicStatement.h │ ├── BlockStatement.cpp │ ├── BlockStatement.h │ ├── ControlStatement.cpp │ ├── ControlStatement.h │ ├── DelStatement.cpp │ ├── DelStatement.h │ ├── ExpressionStatement.cpp │ ├── ExpressionStatement.h │ ├── FlowStatement.cpp │ ├── FlowStatement.h │ ├── ForStatement.cpp │ ├── ForStatement.h │ ├── FunStatement.cpp │ ├── FunStatement.h │ ├── RetStatement.cpp │ ├── RetStatement.h │ ├── Statement.cpp │ ├── Statement.h │ ├── TraceStatement.cpp │ ├── TraceStatement.h │ ├── VarStatement.cpp │ └── VarStatement.h ├── Token.cpp ├── Token.h ├── Value │ ├── BooleanValue.cpp │ ├── BooleanValue.h │ ├── FloatValue.cpp │ ├── FloatValue.h │ ├── FunValue.cpp │ ├── FunValue.h │ ├── IntegerValue.cpp │ ├── IntegerValue.h │ ├── ListValue.cpp │ ├── ListValue.h │ ├── MapValue.cpp │ ├── MapValue.h │ ├── StringValue.cpp │ ├── StringValue.h │ ├── UndefinedValue.cpp │ ├── UndefinedValue.h │ ├── Value.cpp │ └── Value.h ├── binary.cpp ├── binary.h ├── flow.h ├── indirect_compare.h ├── tokenize.cpp ├── tokenize.h ├── unary.cpp └── unary.h ├── test ├── and.hap ├── and.out.expect ├── arithmetic.hap ├── arithmetic.out.expect ├── assign.hap ├── assign.out.expect ├── atomic.hap ├── atomic.out.expect ├── boolean.hap ├── boolean.out.expect ├── comment.hap ├── comment.out.expect ├── curry.hap ├── curry.out.expect ├── exit.hap ├── exit.out.expect ├── for.hap ├── for.out.expect ├── function.hap ├── function.out.expect ├── if.hap ├── if.out.expect ├── lambda.hap ├── lambda.out.expect ├── list-literal.hap ├── list-literal.out.expect ├── list-reference.hap ├── list-reference.out.expect ├── map-literal.hap ├── map-literal.out.expect ├── map-reference.hap ├── map-reference.out.expect ├── map.hap ├── map.out.expect ├── next.hap ├── next.out.expect ├── or.hap ├── or.out.expect ├── redo.hap ├── redo.out.expect ├── run.sh ├── subscript.hap ├── subscript.out.expect ├── trace.hap ├── trace.out.expect ├── var.hap ├── var.out.expect ├── when.hap ├── when.out.expect ├── whenever.hap ├── whenever.out.expect ├── while.hap ├── while.out.expect ├── xor.hap └── xor.out.expect ├── tools └── woc.pl └── unit ├── main.cpp ├── parse.cpp └── tokenize.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | bin 4 | test/*.actual 5 | -------------------------------------------------------------------------------- /Grammar.txt: -------------------------------------------------------------------------------- 1 | program ::= statement* 2 | 3 | statement ::= atomic-statement 4 | | block-statement 5 | | del-statement 6 | | empty-statement 7 | | exit-statement 8 | | for-statement 9 | | fun-statement 10 | | if-statement 11 | | last-statement 12 | | next-statement 13 | | redo-statement 14 | | ret-statement 15 | | trace-statement 16 | | var-statement 17 | | when-statement 18 | | whenever-statement 19 | | while-statement 20 | | expression-statement 21 | 22 | atomic-statement ::= "atomic" statement ";" 23 | 24 | block-statement ::= "{" statement* "}" 25 | 26 | del-statement ::= "del" identifier ";" 27 | 28 | empty-statement ::= ";" 29 | 30 | exit-statement ::= "exit" ";" 31 | 32 | for-statement ::= "for" 33 | "(" 34 | statement 35 | expression 36 | ";" 37 | expression 38 | ")" 39 | statement 40 | 41 | fun-statement ::= "fun" 42 | identifier 43 | "(" 44 | list(identifier) 45 | ")" 46 | statement 47 | 48 | if-statement ::= "if" "(" expression ")" statement 49 | 50 | last-statement ::= "last" ";" 51 | 52 | next-statement ::= "next" ";" 53 | 54 | redo-statement ::= "redo" ";" 55 | 56 | ret-statement ::= "ret" expression? ";" 57 | 58 | trace-statement ::= "trace" expression ";" 59 | 60 | var-statement ::= "var" identifier ("=" expression)? ";" 61 | 62 | when-statement ::= "when" "(" expression ")" statement 63 | 64 | whenever-statement ::= "whenever" "(" expression ")" statement 65 | 66 | while-statement ::= "while" "(" expression ")" statement 67 | 68 | expression-statement ::= expression ";" 69 | 70 | expression ::= value 71 | | expression suffix* 72 | | expression binary-operator expression 73 | | unary-operator expression 74 | 75 | value ::= boolean-value 76 | | float-value 77 | | fun-value 78 | | integer-value 79 | | string-value 80 | | undefined-value 81 | | identifier-expression 82 | 83 | boolean-value ::= "false" | "true" 84 | 85 | float-value ::= ... 86 | 87 | fun-value ::= "lam" identifier? "(" list(identifier) ")" 88 | (":" expression | "{" statement* "}") 89 | 90 | integer-value ::= ... 91 | 92 | string-value ::= ... 93 | 94 | undefined-value ::= "undefined" 95 | 96 | identifier-expression ::= identifier 97 | 98 | suffix ::= call-suffix 99 | | dot-suffix 100 | | subscript-suffix 101 | 102 | call-suffix ::= "(" list(expression) ")" 103 | 104 | dot-suffix ::= "." identifier 105 | 106 | subscript-suffix ::= "[" expression "]" 107 | 108 | unary-operator ::= "+" 109 | | "-" 110 | | "not" 111 | 112 | binary-operator ::= "*" 113 | | "/" 114 | | "mod" 115 | | "+" 116 | | "-" 117 | | "<<" 118 | | ">>" 119 | | "<" 120 | | ">=" 121 | | ">" 122 | | "<=" 123 | | "==" 124 | | "<>" 125 | | "and" 126 | | "xor" 127 | | "or" 128 | | "=" 129 | | "," 130 | 131 | list(x) ::= (x ("," x)*)? 132 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LDFLAGS+=-lc++ 2 | 3 | LIB_PATHS=lib lib/Expression lib/Parser lib/Statement lib/Value 4 | 5 | INCFLAGS=$(addprefix -I,$(LIB_PATHS)) 6 | WARNFLAGS=$(addprefix -W,all error) 7 | CXXFLAGS+=-std=c++11 -stdlib=libc++ $(INCFLAGS) $(WARNFLAGS) 8 | 9 | CPPFLAGS+=-MD -MP 10 | 11 | BIN=./bin 12 | HAP=$(BIN)/hap 13 | HAP_SOURCE_PATHS=hap $(LIB_PATHS) 14 | HAP_SOURCES=$(wildcard $(addsuffix /*.cpp,$(HAP_SOURCE_PATHS))) 15 | HAP_OBJECTS=$(HAP_SOURCES:%.cpp=%.o) 16 | HAP_DEPS=$(HAP_SOURCES:%.cpp=%.d) 17 | 18 | UNIT=$(BIN)/hap-unit 19 | UNIT_SOURCE_PATHS=unit $(LIB_PATHS) 20 | UNIT_SOURCES=$(wildcard $(addsuffix /*.cpp,$(UNIT_SOURCE_PATHS))) 21 | UNIT_OBJECTS=$(UNIT_SOURCES:%.cpp=%.o) 22 | UNIT_DEPS=$(UNIT_SOURCES:%.cpp=%.d) 23 | 24 | TESTER=./test/run.sh 25 | 26 | .PHONY : all 27 | all : build unit-test test 28 | 29 | .PHONY : clean 30 | clean : clean-build clean-deps clean-test 31 | 32 | .PHONY : clean-build 33 | clean-build : 34 | @rm -f $(HAP) 35 | @rm -f $(HAP_OBJECTS) 36 | @rm -f $(UNIT) 37 | @rm -f $(UNIT_OBJECTS) 38 | 39 | .PHONY : clean-deps 40 | clean-deps : 41 | @rm -f $(HAP_DEPS) 42 | @rm -f $(UNIT_DEPS) 43 | 44 | .PHONY : clean-test 45 | clean-test : 46 | @rm -f test/*.actual 47 | 48 | .PHONY : build 49 | build : $(HAP) 50 | 51 | $(HAP) : $(HAP_OBJECTS) 52 | mkdir -p $(BIN) 53 | $(CXX) -o $@ $(LDFLAGS) $(HAP_OBJECTS) 54 | 55 | .PHONY : unit-test 56 | unit-test : $(UNIT) 57 | @$(UNIT) 58 | 59 | $(UNIT) : $(UNIT_OBJECTS) 60 | $(CXX) -o $@ $(LDFLAGS) $(UNIT_OBJECTS) 61 | 62 | TESTS=$(basename $(notdir $(wildcard test/*.hap))) 63 | define TESTRULE 64 | test-$1 : $(HAP) 65 | @$(TESTER) $$(realpath $(HAP)) $1 66 | test : test-$1 67 | endef 68 | .PHONY : $(foreach TEST,$(TESTS),test-$(TEST)) 69 | $(foreach TEST,$(TESTS),$(eval $(call TESTRULE,$(TEST)))) 70 | 71 | .PHONY : loc 72 | loc : 73 | @wc -l $(HAP_SOURCES) | sort -n 74 | 75 | .PHONY : woc 76 | woc : 77 | @./tools/woc.pl $(HAP_SOURCES) 78 | 79 | -include $(HAP_DEPS) 80 | -include $(UNIT_DEPS) 81 | 82 | define DEPENDS_ON_MAKEFILE 83 | $1 : Makefile 84 | 85 | endef 86 | 87 | $(call DEPENDS_ON_MAKEFILE,hap) 88 | $(foreach OBJECT,$(HAP_OBJECTS),$(eval $(call DEPENDS_ON_MAKEFILE,$(OBJECT)))) 89 | $(foreach OBJECT,$(UNIT_OBJECTS),$(eval $(call DEPENDS_ON_MAKEFILE,$(OBJECT)))) 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hap 2 | 3 | **Hap** is a dynamically typed, asynchronous, imperative programming language. 4 | 5 | **This is the original C++ prototype, which is no longer maintained; it’s superseded by [the new Haskell implementation of Hap](https://github.com/evincarofautumn/hap-hs), which has an improved performance model and a graphical mode.** 6 | 7 | Here is a simple example of Hap’s syntax and semantics: 8 | 9 | ``` 10 | var health = 100; 11 | 12 | when (health <= 0) { 13 | print("Goodbye, cruel world!"); 14 | exit; 15 | } 16 | 17 | print("Hello, sweet world!"); 18 | 19 | while (true) 20 | health = health - 1; 21 | ``` 22 | 23 | Hap code reads much like code in other imperative languages. Its syntax is reminiscent of JavaScript. However, note the use of `when`: this is an *asynchronous conditional*. The above code prints: 24 | 25 | ``` 26 | Hello, sweet world! 27 | Goodbye, cruel world! 28 | ``` 29 | 30 | *Synchronous* flow control statements such as `if`, `while`, and `for` are evaluated immediately. If the condition is true *when the statement is reached*, then the body is evaluated immediately, before proceeding to subsequent statements. 31 | 32 | *Asynchronous* flow control statements such as `when` are evaluated concurrently. The flow of control proceeds to subsequent statements immediately; only when the conditional actually becomes true is the body evaluated. 33 | 34 | This allows programs to be structured implicitly in terms of events and time-based constraints. You are not forced to subscribe to event handlers for predefined conditions—rather, you can create ad-hoc conditions as needed, and this is syntactically lightweight. 35 | 36 | # Types 37 | 38 | 39 | 40 | 41 | 42 | 44 | 45 | 46 | 47 | 49 | 50 | 51 | 52 | 54 | 55 | 56 | 57 | 74 | 75 | 76 | 77 | 95 | 96 |
TypeExamples
int
0
 43 | 1234
bool
false
 48 | true
text
""
 53 | "scare"
list
[]
 58 | 
 59 | [1, 2, 3, 4, 5]
 60 | 
 61 | [
 62 |   "this",
 63 |   "that",
 64 |   "and",
 65 |   "the",
 66 |   "other",
 67 |   "thing",
 68 | ]
 69 | 
 70 | [ [ +cos(x), -sin(x) ],
 71 |   [ +sin(x), +cos(x) ] ]
 72 | 
 73 | [0, false, ""]
map
{}
 78 | 
 79 | { "one": 1, "two": 2, "three": 3 }
 80 | 
 81 | [
 82 |   {
 83 |     en: "one",
 84 |     fr: "un",
 85 |   },
 86 |   {
 87 |     en: "two",
 88 |     fr: "deux",
 89 |   },
 90 |   {
 91 |     en: "three",
 92 |     fr: "trois",
 93 |   },
 94 | ]
97 | 98 | # Statements 99 | 100 | ## General 101 | 102 | 103 | 104 | 105 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 |
StatementDescription
var NAME;
106 | var NAME = EXPR;
Creates a lexically scoped variable named NAME. Initializes it to EXPR, or gives it an initial value of undef if EXPR is not specified.
fun NAME(PARAM, PARAM, ...) STAT
Creates a lexically scoped function named NAME with a body given by STAT using the given parameters.
EXPR;
Synchronous. Evaluates EXPR and discards the result.
{ STAT... }
Synchronous. Evaluates a block of statements as a unit. Introduces a new lexical scope.
exit
Exits the program entirely.
last
Exits the current loop.
next
Jumps to the next iteration of the current loop, re-evaluating the loop condition.
redo
Redoes the current iteration of the current loop, without re-evaluating the loop condition (or step, in the case of for).
138 | 139 | ## Flow Control 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 183 | 184 |
StatementSynchronicityHow many times STAT is evaluatedWhen STAT is evaluated
if EXPR STAT
SynchronousOnceIf EXPR is true when the statement is reached.
when EXPR STAT
AsynchronousOnceThe first time EXPR becomes true; immediately if EXPR is already true.
whenever EXPR STAT
AsynchronousOnceEvery time EXPR becomes true; immediately if EXPR is already true.
while EXPR STAT
SynchronousRepeatedlyAs long as EXPR remains true; never if EXPR is already false.
for (INIT; COND; STEP) STAT
SynchronousRepeatedly

Equivalent to: 177 |

INIT;
178 | while (COND) {
179 |   STAT;
180 |   STEP;
181 | }
182 |

Except that variables declared in INIT are local to the loop, and STEP is evaluated even when the next statement is used.

185 | -------------------------------------------------------------------------------- /hap/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Interpreter.h" 2 | #include "Parser.h" 3 | #include "Statement.h" 4 | #include "tokenize.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace hap; 13 | using namespace std; 14 | 15 | int main(int argc, char** argv) try { 16 | --argc; 17 | ++argv; 18 | if (argc != 1) 19 | throw runtime_error("Bad command line."); 20 | ifstream input(argv[0]); 21 | const auto global = make_shared(); 22 | Interpreter(global).run 23 | (Parser(tokenize(input), global).accept_program()); 24 | } catch (const exception& error) { 25 | cerr << error.what() << '\n'; 26 | return 1; 27 | } 28 | -------------------------------------------------------------------------------- /lib/Atomic.cpp: -------------------------------------------------------------------------------- 1 | #include "Atomic.h" 2 | 3 | #include "Context.h" 4 | 5 | namespace hap { 6 | 7 | Atomic::Atomic(Context& context) : context(context), atomic(context.atomic) { 8 | context.atomic = true; 9 | } 10 | 11 | Atomic::~Atomic() { 12 | context.atomic = atomic; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lib/Atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_ATOMIC_H 2 | #define HAP_ATOMIC_H 3 | 4 | namespace hap { 5 | 6 | class Context; 7 | 8 | class Atomic { 9 | public: 10 | Atomic(Context&); 11 | ~Atomic(); 12 | private: 13 | Context& context; 14 | bool atomic; 15 | }; 16 | 17 | } 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /lib/Context.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Context.h" 3 | 4 | #include "Atomic.h" 5 | #include "BooleanValue.h" 6 | 7 | using namespace std; 8 | 9 | namespace hap { 10 | 11 | Context::Context() : atomic(false) {} 12 | 13 | void Context::interrupt 14 | (const shared_ptr environment) { 15 | if (atomic) 16 | return; 17 | vector> statements; 18 | for (auto listener = listeners.begin(); 19 | listener != listeners.end(); 20 | ++listener) { 21 | if (dead(listener)) 22 | continue; 23 | const auto& condition(listener->first); 24 | auto& handler(listener->second); 25 | const bool value = atomic_eval_as 26 | (condition, *this, handler.environment)->value; 27 | switch (handler.behavior) { 28 | case ONCE: 29 | if (value) { 30 | statements.push_back(handler.statement); 31 | dead_listeners.push_back(listener); 32 | } 33 | break; 34 | case REPEATEDLY: 35 | if (value && !handler.previous) 36 | statements.push_back(handler.statement); 37 | break; 38 | } 39 | handler.previous = value; 40 | } 41 | for (const auto& dead_listener : dead_listeners) 42 | listeners.erase(dead_listener); 43 | dead_listeners.clear(); 44 | for (const auto& statement : statements) 45 | statement->execute(*this, environment); 46 | } 47 | 48 | bool Context::dead(ListenerMap::iterator listener) const { 49 | return find(begin(dead_listeners), end(dead_listeners), listener) 50 | != dead_listeners.end(); 51 | } 52 | 53 | void Context::listen 54 | (const shared_ptr condition, 55 | const Behavior behavior, 56 | const shared_ptr body, 57 | const shared_ptr environment) { 58 | Handler handler{behavior, false, body, environment}; 59 | listeners.insert(make_pair(condition, handler)); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /lib/Context.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_CONTEXT_H 2 | #define HAP_CONTEXT_H 3 | 4 | #include "Statement.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace hap { 12 | 13 | class Context { 14 | public: 15 | friend class Atomic; 16 | enum Behavior { 17 | ONCE, 18 | REPEATEDLY, 19 | }; 20 | Context(); 21 | void interrupt(std::shared_ptr); 22 | void listen 23 | (std::shared_ptr, 24 | Behavior, 25 | std::shared_ptr, 26 | std::shared_ptr); 27 | private: 28 | struct Handler { 29 | Behavior behavior; 30 | bool previous; 31 | std::shared_ptr statement; 32 | std::shared_ptr environment; 33 | }; 34 | typedef std::map, Handler> ListenerMap; 35 | bool dead(ListenerMap::iterator) const; 36 | ListenerMap listeners; 37 | std::vector dead_listeners; 38 | bool atomic; 39 | }; 40 | 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /lib/Environment.cpp: -------------------------------------------------------------------------------- 1 | #include "Environment.h" 2 | #include "UndefinedValue.h" 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace hap { 10 | 11 | Environment::Environment(const weak_ptr parent) 12 | : parent(parent) {} 13 | 14 | void Environment::define(const string& name, shared_ptr value) { 15 | variables[name] = value; 16 | } 17 | 18 | void Environment::undefine(const std::string& name) { 19 | const auto existing(variables.find(name)); 20 | if (existing == variables.end()) { 21 | if (const auto held_parent = parent.lock()) 22 | held_parent->undefine(name); 23 | } else { 24 | variables.erase(existing); 25 | } 26 | } 27 | 28 | shared_ptr& Environment::operator[](const string& name) { 29 | const auto existing(variables.find(name)); 30 | if (existing == variables.end()) { 31 | if (const auto held_parent = parent.lock()) 32 | return (*held_parent)[name]; 33 | variables[name].reset(new UndefinedValue()); 34 | return variables[name]; 35 | } 36 | return existing->second; 37 | } 38 | 39 | ostream& operator<< 40 | (ostream& stream, const Environment& environment) { 41 | stream << "Environment@" << &environment << "{\n"; 42 | for (const auto& pair : environment.variables) 43 | stream << pair.first << " = " << pair.second << "\n"; 44 | stream << "}, parent "; 45 | if (const auto parent = environment.parent.lock()) { 46 | stream << *parent; 47 | } else { 48 | stream << "null"; 49 | } 50 | return stream << "\n"; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /lib/Environment.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_ENVIRONMENT_H 2 | #define HAP_ENVIRONMENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace hap { 9 | 10 | class Value; 11 | 12 | class Environment { 13 | public: 14 | Environment(std::weak_ptr = std::weak_ptr()); 15 | void define(const std::string&, std::shared_ptr); 16 | void undefine(const std::string&); 17 | std::shared_ptr& operator[](const std::string&); 18 | friend std::ostream& operator<<(std::ostream&, const Environment&); 19 | private: 20 | Environment(const Environment&) = delete; 21 | std::weak_ptr parent; 22 | std::map> variables; 23 | }; 24 | 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /lib/Expression/BinaryExpression.cpp: -------------------------------------------------------------------------------- 1 | #include "BinaryExpression.h" 2 | #include "Value.h" 3 | 4 | #include 5 | 6 | using namespace std; 7 | 8 | namespace hap { 9 | 10 | shared_ptr BinaryExpression::eval 11 | (Context& context, 12 | const shared_ptr environment) const { 13 | if (operator_.binary) 14 | return operator_.binary(context, environment, left, right); 15 | ostringstream message; 16 | message << "unimplemented binary operator " << operator_; 17 | throw runtime_error(message.str()); 18 | } 19 | 20 | bool BinaryExpression::equal(const Expression& expression) const { 21 | if (const auto that 22 | = dynamic_cast(&expression)) { 23 | return *left == *that->left 24 | && *right == *that->right; 25 | } 26 | return false; 27 | } 28 | 29 | void BinaryExpression::write(ostream& stream) const { 30 | stream << '(' << *left << ' ' << operator_ << ' ' << *right << ')'; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /lib/Expression/BinaryExpression.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_BINARYEXPRESSION_H 2 | #define HAP_BINARYEXPRESSION_H 3 | 4 | #include "Expression.h" 5 | 6 | #include "Operator.h" 7 | 8 | namespace hap { 9 | 10 | class BinaryExpression : public Expression { 11 | public: 12 | BinaryExpression 13 | (const Operator& operator_, 14 | Expression* left, 15 | Expression* right) 16 | : operator_(operator_), 17 | left(left), 18 | right(right) {} 19 | BinaryExpression 20 | (const Operator& operator_, 21 | std::shared_ptr left, 22 | std::shared_ptr right) 23 | : operator_(operator_), 24 | left(left), 25 | right(right) {} 26 | virtual std::shared_ptr eval 27 | (Context&, std::shared_ptr) const final override; 28 | protected: 29 | virtual bool equal(const Expression&) const final override; 30 | virtual void write(std::ostream&) const final override; 31 | private: 32 | Operator operator_; 33 | std::shared_ptr left; 34 | std::shared_ptr right; 35 | }; 36 | 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /lib/Expression/CallExpression.cpp: -------------------------------------------------------------------------------- 1 | #include "CallExpression.h" 2 | 3 | #include "FunValue.h" 4 | #include "Value.h" 5 | #include "indirect_compare.h" 6 | 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | namespace hap { 13 | 14 | CallExpression::CallExpression 15 | (Expression* const function, 16 | std::initializer_list expressions) 17 | : function(function) { 18 | for (const auto& expression : expressions) 19 | this->expressions.push_back(shared_ptr(expression)); 20 | } 21 | 22 | CallExpression::CallExpression 23 | (shared_ptr function, 24 | vector>&& expressions) 25 | : function(function), 26 | expressions(expressions) {} 27 | 28 | shared_ptr CallExpression::eval 29 | (Context& context, const shared_ptr environment) const { 30 | const auto value 31 | (eval_as(function, context, environment)); 32 | return value->call(context, expressions); 33 | } 34 | 35 | bool CallExpression::equal(const Expression& expression) const { 36 | if (const auto that 37 | = dynamic_cast(&expression)) { 38 | return *function == *that->function 39 | && expressions.size() == that->expressions.size() 40 | && std::equal 41 | (begin(expressions), end(expressions), 42 | begin(that->expressions), indirect_equal()); 43 | } 44 | return false; 45 | } 46 | 47 | void CallExpression::write(ostream& stream) const { 48 | stream << *function << "("; 49 | for (const auto& expression : expressions) 50 | stream << *expression << ", "; 51 | stream << ")"; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /lib/Expression/CallExpression.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_CALLEXPRESSION_H 2 | #define HAP_CALLEXPRESSION_H 3 | 4 | #include "Expression.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class CallExpression : public Expression { 11 | public: 12 | CallExpression(Expression*, std::initializer_list); 13 | CallExpression 14 | (std::shared_ptr, 15 | std::vector>&&); 16 | virtual std::shared_ptr eval 17 | (Context&, std::shared_ptr) const final override; 18 | protected: 19 | virtual bool equal(const Expression&) const final override; 20 | virtual void write(std::ostream&) const final override; 21 | private: 22 | std::shared_ptr function; 23 | std::vector> expressions; 24 | }; 25 | 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /lib/Expression/DotExpression.cpp: -------------------------------------------------------------------------------- 1 | #include "DotExpression.h" 2 | 3 | #include "MapValue.h" 4 | #include "StringValue.h" 5 | 6 | #include 7 | 8 | using namespace std; 9 | 10 | namespace hap { 11 | 12 | DotExpression::DotExpression 13 | (Expression* const expression, 14 | const string& key) 15 | : expression(expression), 16 | key(key) {} 17 | 18 | DotExpression::DotExpression 19 | (shared_ptr expression, 20 | const string& key) 21 | : expression(expression), 22 | key(key) {} 23 | 24 | shared_ptr DotExpression::eval 25 | (Context& context, const shared_ptr environment) const { 26 | const auto map(eval_as(expression, context, environment)); 27 | return (*map)[make_shared(key)]; 28 | } 29 | 30 | bool DotExpression::equal(const Expression& expression) const { 31 | if (const auto that 32 | = dynamic_cast(&expression)) { 33 | return *this->expression == *that->expression 34 | && key == that->key; 35 | } 36 | return false; 37 | } 38 | 39 | void DotExpression::write(ostream& stream) const { 40 | stream << *expression << "." << key; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /lib/Expression/DotExpression.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_DOTEXPRESSION_H 2 | #define HAP_DOTEXPRESSION_H 3 | 4 | #include "Expression.h" 5 | 6 | #include "binary.h" 7 | 8 | #include 9 | 10 | namespace hap { 11 | 12 | class DotExpression : public Expression { 13 | public: 14 | friend Operator::Binary binary::assign; 15 | DotExpression(Expression*, const std::string&); 16 | DotExpression(std::shared_ptr, const std::string&); 17 | virtual std::shared_ptr eval 18 | (Context&, std::shared_ptr) const final override; 19 | protected: 20 | virtual bool equal(const Expression&) const final override; 21 | virtual void write(std::ostream&) const final override; 22 | private: 23 | std::shared_ptr expression; 24 | std::string key; 25 | }; 26 | 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /lib/Expression/Expression.cpp: -------------------------------------------------------------------------------- 1 | #include "Expression.h" 2 | #include "Environment.h" 3 | #include "flow.h" 4 | 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | namespace hap { 11 | 12 | Expression::~Expression() {} 13 | 14 | bool operator==(const Expression& a, const Expression& b) { 15 | return a.equal(b); 16 | } 17 | 18 | ostream& operator<<(ostream& stream, const Expression& expression) { 19 | expression.write(stream); 20 | return stream; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /lib/Expression/Expression.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_EXPRESSION_H 2 | #define HAP_EXPRESSION_H 3 | 4 | #include 5 | #include 6 | 7 | namespace hap { 8 | 9 | class Context; 10 | class Environment; 11 | class Value; 12 | 13 | class Expression { 14 | public: 15 | virtual ~Expression(); 16 | virtual std::shared_ptr eval 17 | (Context&, std::shared_ptr) const = 0; 18 | virtual bool equal(const Expression&) const = 0; 19 | friend bool operator==(const Expression&, const Expression&); 20 | friend std::ostream& operator<<(std::ostream&, const Expression&); 21 | protected: 22 | virtual void write(std::ostream&) const = 0; 23 | }; 24 | 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /lib/Expression/IdentifierExpression.cpp: -------------------------------------------------------------------------------- 1 | #include "IdentifierExpression.h" 2 | 3 | #include "Environment.h" 4 | #include "Value.h" 5 | 6 | using namespace std; 7 | 8 | namespace hap { 9 | 10 | shared_ptr IdentifierExpression::eval 11 | (Context& context, const shared_ptr environment) const { 12 | return (*environment)[identifier]; 13 | } 14 | 15 | bool IdentifierExpression::equal 16 | (const Expression& expression) const { 17 | if (const auto that 18 | = dynamic_cast(&expression)) { 19 | return identifier == that->identifier; 20 | } 21 | return false; 22 | } 23 | 24 | void IdentifierExpression::write(ostream& stream) const { 25 | stream << identifier; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /lib/Expression/IdentifierExpression.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_IDENTIFIEREXPRESSION_H 2 | #define HAP_IDENTIFIEREXPRESSION_H 3 | 4 | #include "Expression.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class IdentifierExpression : public Expression { 11 | public: 12 | IdentifierExpression(const std::string& identifier) 13 | : identifier(identifier) {} 14 | virtual std::shared_ptr eval 15 | (Context&, std::shared_ptr) const final override; 16 | std::string identifier; 17 | protected: 18 | virtual bool equal(const Expression&) const final override; 19 | virtual void write(std::ostream&) const final override; 20 | }; 21 | 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /lib/Expression/ListExpression.cpp: -------------------------------------------------------------------------------- 1 | #include "ListExpression.h" 2 | 3 | #include "ListValue.h" 4 | #include "indirect_compare.h" 5 | 6 | #include 7 | 8 | using namespace std; 9 | 10 | namespace hap { 11 | 12 | shared_ptr ListExpression::eval 13 | (Context& context, const shared_ptr environment) const { 14 | const auto list = make_shared(); 15 | for (const auto& expression : expressions) 16 | list->push(expression->eval(context, environment)); 17 | return static_pointer_cast(list); 18 | } 19 | 20 | bool ListExpression::equal(const Expression& expression) const { 21 | if (const auto that 22 | = dynamic_cast(&expression)) { 23 | return expressions.size() == that->expressions.size() 24 | && std::equal 25 | (begin(expressions), end(expressions), 26 | begin(that->expressions), indirect_equal()); 27 | } 28 | return false; 29 | } 30 | 31 | void ListExpression::write(ostream& stream) const { 32 | stream << "[ "; 33 | for (const auto& expression : expressions) 34 | stream << *expression << ", "; 35 | stream << ']'; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /lib/Expression/ListExpression.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_LISTEXPRESSION_H 2 | #define HAP_LISTEXPRESSION_H 3 | 4 | #include "Expression.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class ListExpression : public Expression { 11 | public: 12 | ListExpression() {} 13 | void push(std::shared_ptr expression) { 14 | expressions.push_back(expression); 15 | } 16 | virtual std::shared_ptr eval 17 | (Context&, std::shared_ptr) const final override; 18 | protected: 19 | virtual bool equal(const Expression&) const final override; 20 | virtual void write(std::ostream&) const final override; 21 | private: 22 | std::vector> expressions; 23 | }; 24 | 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /lib/Expression/MapExpression.cpp: -------------------------------------------------------------------------------- 1 | #include "MapExpression.h" 2 | 3 | #include "MapValue.h" 4 | #include "indirect_compare.h" 5 | 6 | #include 7 | 8 | using namespace std; 9 | 10 | namespace hap { 11 | 12 | shared_ptr MapExpression::eval 13 | (Context& context, const shared_ptr environment) const { 14 | const auto map = make_shared(); 15 | for (const auto& pair : pairs) { 16 | auto key(pair.first->eval(context, environment)); 17 | auto value(pair.second->eval(context, environment)); 18 | map->insert(key, value); 19 | } 20 | return static_pointer_cast(map); 21 | } 22 | 23 | bool MapExpression::equal(const Expression& expression) const { 24 | if (const auto that 25 | = dynamic_cast(&expression)) { 26 | return pairs.size() == that->pairs.size() 27 | && std::equal 28 | (begin(pairs), end(pairs), 29 | begin(that->pairs), 30 | pair_indirect_equal()); 31 | } 32 | return false; 33 | } 34 | 35 | void MapExpression::write(ostream& stream) const { 36 | stream << "{ "; 37 | for (const auto& pair : pairs) 38 | stream << *pair.first << ": " << *pair.second << ", "; 39 | stream << '}'; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /lib/Expression/MapExpression.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_MAPEXPRESSION_H 2 | #define HAP_MAPEXPRESSION_H 3 | 4 | #include "Expression.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class MapExpression : public Expression { 11 | public: 12 | MapExpression() {} 13 | void insert 14 | (std::shared_ptr key, 15 | std::shared_ptr value) { 16 | pairs.insert(std::make_pair(key, value)); 17 | } 18 | virtual std::shared_ptr eval 19 | (Context&, const std::shared_ptr) const final override; 20 | protected: 21 | virtual bool equal(const Expression&) const final override; 22 | virtual void write(std::ostream&) const final override; 23 | private: 24 | std::map 25 | , 26 | std::shared_ptr> pairs; 27 | }; 28 | 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /lib/Expression/SubscriptExpression.cpp: -------------------------------------------------------------------------------- 1 | #include "SubscriptExpression.h" 2 | 3 | #include "IntegerValue.h" 4 | #include "ListValue.h" 5 | #include "MapValue.h" 6 | #include "Value.h" 7 | 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | namespace hap { 14 | 15 | SubscriptExpression::SubscriptExpression 16 | (Expression* const expression, Expression* const subscript) 17 | : expression(expression), 18 | subscript(subscript) {} 19 | 20 | SubscriptExpression::SubscriptExpression 21 | (shared_ptr expression, 22 | shared_ptr subscript) 23 | : expression(expression), 24 | subscript(subscript) {} 25 | 26 | shared_ptr SubscriptExpression::eval 27 | (Context& context, const shared_ptr environment) const { 28 | const auto value(expression->eval(context, environment)); 29 | const auto key(subscript->eval(context, environment)); 30 | if (const auto list = dynamic_cast(value.get())) { 31 | key->assert_type(Value::INTEGER); 32 | return (*list)[static_cast(key.get())->value]; 33 | } 34 | if (const auto map = dynamic_cast(value.get())) 35 | return (*map)[key]; 36 | ostringstream message; 37 | message << "expected list or map but got " << value->type(); 38 | throw runtime_error(message.str()); 39 | } 40 | 41 | bool SubscriptExpression::equal(const Expression& expression) const { 42 | if (const auto that 43 | = dynamic_cast(&expression)) { 44 | return *this->expression == *that->expression 45 | && *subscript == *that->subscript; 46 | } 47 | return false; 48 | } 49 | 50 | void SubscriptExpression::write(ostream& stream) const { 51 | stream << *expression << "[" << *subscript << "]"; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /lib/Expression/SubscriptExpression.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_SUBSCRIPTEXPRESSION_H 2 | #define HAP_SUBSCRIPTEXPRESSION_H 3 | 4 | #include "Expression.h" 5 | 6 | #include "binary.h" 7 | 8 | #include 9 | 10 | namespace hap { 11 | 12 | class SubscriptExpression : public Expression { 13 | public: 14 | friend Operator::Binary binary::assign; 15 | SubscriptExpression(Expression*, Expression*); 16 | SubscriptExpression 17 | (std::shared_ptr, 18 | std::shared_ptr); 19 | virtual std::shared_ptr eval 20 | (Context&, std::shared_ptr) const final override; 21 | protected: 22 | virtual bool equal(const Expression&) const final override; 23 | virtual void write(std::ostream&) const final override; 24 | private: 25 | std::shared_ptr expression; 26 | std::shared_ptr subscript; 27 | }; 28 | 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /lib/Expression/UnaryExpression.cpp: -------------------------------------------------------------------------------- 1 | #include "UnaryExpression.h" 2 | #include "Value.h" 3 | 4 | #include 5 | 6 | using namespace std; 7 | 8 | namespace hap { 9 | 10 | shared_ptr UnaryExpression::eval 11 | (Context& context, const shared_ptr environment) const { 12 | if (operator_.unary) 13 | return operator_.unary(context, environment, expression); 14 | ostringstream message; 15 | message << "unimplemented unary operator " << operator_; 16 | throw runtime_error(message.str()); 17 | } 18 | 19 | bool UnaryExpression::equal(const Expression& expression) const { 20 | if (const auto that 21 | = dynamic_cast(&expression)) { 22 | return *this->expression == *that->expression; 23 | } 24 | return false; 25 | } 26 | 27 | void UnaryExpression::write(ostream& stream) const { 28 | stream << '(' << operator_ << ' ' << *expression << ')'; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /lib/Expression/UnaryExpression.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_UNARYEXPRESSION_H 2 | #define HAP_UNARYEXPRESSION_H 3 | 4 | #include "Expression.h" 5 | 6 | #include "Operator.h" 7 | 8 | namespace hap { 9 | 10 | class UnaryExpression : public Expression { 11 | public: 12 | UnaryExpression 13 | (const Operator& operator_, 14 | std::shared_ptr expression) 15 | : operator_(operator_), 16 | expression(expression) {} 17 | virtual std::shared_ptr eval 18 | (Context&, std::shared_ptr) const final override; 19 | protected: 20 | virtual bool equal(const Expression&) const final override; 21 | virtual void write(std::ostream&) const final override; 22 | private: 23 | Operator operator_; 24 | std::shared_ptr expression; 25 | }; 26 | 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /lib/Interpreter.cpp: -------------------------------------------------------------------------------- 1 | #include "Interpreter.h" 2 | 3 | #include "Expression.h" 4 | #include "Statement.h" 5 | #include "Value.h" 6 | #include "flow.h" 7 | 8 | #include 9 | 10 | using namespace std; 11 | 12 | namespace hap { 13 | 14 | Interpreter::Interpreter(const shared_ptr environment) 15 | : global_environment(environment) {} 16 | 17 | void Interpreter::run(shared_ptr statement) { 18 | try { 19 | statement->execute(global_context, global_environment); 20 | } catch (const flow::Exit&) { 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lib/Interpreter.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_INTERPRETER_H 2 | #define HAP_INTERPRETER_H 3 | 4 | #include "Context.h" 5 | #include "Environment.h" 6 | 7 | #include 8 | 9 | namespace hap { 10 | 11 | class Statement; 12 | 13 | class Interpreter { 14 | public: 15 | Interpreter(std::shared_ptr); 16 | void run(std::shared_ptr); 17 | private: 18 | Context global_context; 19 | std::shared_ptr global_environment; 20 | }; 21 | 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /lib/Operator.cpp: -------------------------------------------------------------------------------- 1 | #include "Operator.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace hap { 8 | 9 | ostream& operator<<(ostream& stream, const Operator& operator_) { 10 | return stream << operator_.operator_; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lib/Operator.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_OPERATOR_H 2 | #define HAP_OPERATOR_H 3 | 4 | #include 5 | #include 6 | 7 | namespace hap { 8 | 9 | class Context; 10 | class Environment; 11 | class Expression; 12 | class Value; 13 | 14 | class Operator { 15 | private: 16 | typedef std::shared_ptr ValuePointer; 17 | typedef std::shared_ptr ExpressionPointer; 18 | typedef std::shared_ptr EnvironmentPointer; 19 | public: 20 | typedef ValuePointer Unary 21 | (Context&, EnvironmentPointer, const ExpressionPointer&); 22 | typedef ValuePointer Binary 23 | (Context&, EnvironmentPointer, 24 | const ExpressionPointer&, const ExpressionPointer&); 25 | enum Arity { 26 | SENTINEL, 27 | UNARY, 28 | BINARY 29 | }; 30 | enum Associativity { 31 | LEFT, 32 | RIGHT 33 | }; 34 | enum Precedence { 35 | ASSIGNMENT, 36 | OR, 37 | XOR, 38 | AND, 39 | RELATIONAL, 40 | SHIFTING, 41 | ADDITIVE, 42 | MULTIPLICATIVE, 43 | TIGHTEST 44 | }; 45 | Operator() : arity(SENTINEL) {} 46 | Operator 47 | (Arity arity, 48 | const std::string& operator_, 49 | Associativity associativity, 50 | Precedence precedence, 51 | Binary* binary) 52 | : arity(arity), 53 | operator_(operator_), 54 | associativity(associativity), 55 | precedence(precedence), 56 | unary(0), 57 | binary(binary) {} 58 | Operator 59 | (Arity arity, 60 | const std::string& operator_, 61 | Associativity associativity, 62 | Precedence precedence, 63 | Unary* unary) 64 | : arity(arity), 65 | operator_(operator_), 66 | associativity(associativity), 67 | precedence(precedence), 68 | unary(unary), 69 | binary(0) {} 70 | Arity arity; 71 | std::string operator_; 72 | Associativity associativity; 73 | Precedence precedence; 74 | Unary* unary; 75 | Binary* binary; 76 | }; 77 | 78 | inline bool operator<(const Operator& a, const Operator& b) { 79 | return 80 | a.arity == Operator::BINARY && b.arity == Operator::BINARY 81 | ? a.precedence < b.precedence 82 | || (b.associativity == Operator::LEFT && a.precedence == b.precedence) 83 | : a.arity == Operator::BINARY && b.arity == Operator::UNARY 84 | ? !(b.precedence < a.precedence) 85 | : false; 86 | } 87 | 88 | std::ostream& operator<<(std::ostream&, const Operator&); 89 | 90 | } 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /lib/Parser/Parser.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser.h" 2 | #include "Statement.h" 3 | 4 | using namespace std; 5 | 6 | namespace hap { 7 | 8 | Parser::Parser 9 | (const vector& input, 10 | const shared_ptr global) 11 | : tokens(input), 12 | current(tokens.begin()), 13 | global(global) {} 14 | 15 | shared_ptr 16 | Parser::accept_program() { 17 | shared_ptr statements(accept_statements(global)); 18 | if (!at_end()) 19 | expected("end of input"); 20 | return statements; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /lib/Parser/Parser.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_PARSE_H 2 | #define HAP_PARSE_H 3 | 4 | #include "Environment.h" 5 | #include "Token.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace hap { 12 | 13 | class Expression; 14 | class Operator; 15 | class Statement; 16 | 17 | class Parser { 18 | private: 19 | typedef std::shared_ptr AcceptExpression 20 | (std::shared_ptr); 21 | typedef bool AcceptOperator(Operator&); 22 | typedef std::shared_ptr AcceptStatement 23 | (std::shared_ptr); 24 | typedef std::shared_ptr AcceptSuffix 25 | (std::shared_ptr, std::shared_ptr); 26 | public: 27 | Parser(const std::vector&, std::shared_ptr); 28 | std::shared_ptr accept_program(); 29 | private: 30 | AcceptExpression 31 | accept_expression, 32 | accept_value_expression, 33 | accept_identifier_expression, 34 | // ---- 35 | accept_boolean_value, 36 | accept_float_value, 37 | accept_fun_value, 38 | accept_integer_value, 39 | accept_string_value, 40 | accept_undefined_value; 41 | AcceptOperator 42 | accept_binary_operator, 43 | accept_unary_operator; 44 | AcceptStatement 45 | accept_statement, 46 | accept_statements, 47 | // ---- 48 | accept_atomic_statement, 49 | accept_block_statement, 50 | accept_del_statement, 51 | accept_empty_statement, 52 | accept_exit_statement, 53 | accept_for_statement, 54 | accept_fun_statement, 55 | accept_if_statement, 56 | accept_last_statement, 57 | accept_next_statement, 58 | accept_redo_statement, 59 | accept_ret_statement, 60 | accept_trace_statement, 61 | accept_var_statement, 62 | accept_when_statement, 63 | accept_whenever_statement, 64 | accept_while_statement, 65 | // ---- 66 | accept_expression_statement; 67 | AcceptSuffix 68 | accept_suffix, 69 | // ---- 70 | accept_dot_suffix, 71 | accept_call_suffix, 72 | accept_subscript_suffix; 73 | template 74 | typename std::shared_ptr first 75 | (const std::shared_ptr environment, 76 | First function, Rest&&... rest) { 77 | auto result((this->*function)(environment)); 78 | if (result) 79 | return result; 80 | return first(environment, std::forward(rest)...); 81 | } 82 | template 83 | typename std::shared_ptr first 84 | (const std::shared_ptr environment, Only function) { 85 | return (this->*function)(environment); 86 | } 87 | void infix_expression 88 | (std::shared_ptr, 89 | std::stack&, 90 | std::stack>&); 91 | void infix_subexpression 92 | (std::shared_ptr, 93 | std::stack&, 94 | std::stack>&); 95 | void pop_operator 96 | (std::stack&, 97 | std::stack>&); 98 | void push_operator 99 | (const Operator&, 100 | std::stack&, 101 | std::stack>&); 102 | typedef std::vector Tokens; 103 | const Tokens tokens; 104 | Tokens::const_iterator current; 105 | void backtrack(); 106 | bool peek(Token::Type) const; 107 | bool peek(const Token&) const; 108 | bool accept(Token::Type); 109 | bool accept(Token::Type, Token&); 110 | bool accept(const Token&); 111 | void expect(Token::Type); 112 | void expect(Token::Type, Token&); 113 | void expect(const Token&); 114 | void expected(const std::string&) const; 115 | void expected(Token::Type) const; 116 | void expected(const Token&) const; 117 | bool at_end() const; 118 | template 119 | std::shared_ptr 120 | accept_flow_statement(std::shared_ptr, const std::string&); 121 | template 122 | std::shared_ptr 123 | accept_control_statement(std::shared_ptr, const std::string&); 124 | std::shared_ptr global; 125 | }; 126 | 127 | template 128 | std::shared_ptr 129 | Parser::accept_control_statement 130 | (const std::shared_ptr environment, 131 | const std::string& keyword) { 132 | if (!accept(Token(Token::IDENTIFIER, keyword))) 133 | return nullptr; 134 | expect(Token::SEMICOLON); 135 | return std::make_shared(); 136 | } 137 | 138 | template 139 | std::shared_ptr 140 | Parser::accept_flow_statement 141 | (const std::shared_ptr environment, 142 | const std::string& keyword) { 143 | using namespace std; 144 | if (!accept(Token(Token::IDENTIFIER, keyword))) 145 | return nullptr; 146 | shared_ptr expression; 147 | expect(Token::LEFT_PARENTHESIS); 148 | if (!(expression = accept_expression(environment))) 149 | expected("expression"); 150 | expect(Token::RIGHT_PARENTHESIS); 151 | shared_ptr statement; 152 | if (!(statement = accept_statement(environment))) 153 | expected("statement"); 154 | return make_shared(expression, statement); 155 | } 156 | 157 | } 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /lib/Parser/core.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace hap { 8 | 9 | void Parser::backtrack() { 10 | --current; 11 | } 12 | 13 | bool Parser::peek(Token::Type type) const { 14 | return !at_end() && current->type == type; 15 | } 16 | 17 | bool Parser::peek(const Token& token) const { 18 | return !at_end() && *current == token; 19 | } 20 | 21 | bool Parser::accept(Token::Type type) { 22 | if (!peek(type)) 23 | return false; 24 | ++current; 25 | return true; 26 | } 27 | 28 | bool Parser::accept(Token::Type type, Token& result) { 29 | if (!peek(type)) 30 | return false; 31 | result = *current++; 32 | return true; 33 | } 34 | 35 | bool Parser::accept(const Token& token) { 36 | if (!peek(token)) 37 | return false; 38 | ++current; 39 | return true; 40 | } 41 | 42 | void Parser::expect(Token::Type type) { 43 | if (!accept(type)) 44 | expected(type); 45 | } 46 | 47 | void Parser::expect(Token::Type type, Token& result) { 48 | if (!accept(type, result)) 49 | expected(type); 50 | } 51 | 52 | void Parser::expect(const Token& token) { 53 | if (!accept(token)) 54 | expected(token); 55 | } 56 | 57 | void Parser::expected(const string& abstract) const { 58 | ostringstream message; 59 | message << "expected " << abstract << " but got "; 60 | if (at_end()) { 61 | message << "end of input"; 62 | } else { 63 | message << *current; 64 | } 65 | throw runtime_error(message.str()); 66 | } 67 | 68 | void Parser::expected(Token::Type type) const { 69 | ostringstream message; 70 | message << "expected " << type << " but got "; 71 | if (at_end()) { 72 | message << "end of input"; 73 | } else { 74 | message << *current; 75 | } 76 | throw runtime_error(message.str()); 77 | } 78 | 79 | void Parser::expected(const Token& token) const { 80 | ostringstream message; 81 | message << "expected " << token << " but got "; 82 | if (at_end()) { 83 | message << "end of input"; 84 | } else { 85 | message << *current; 86 | } 87 | throw runtime_error(message.str()); 88 | } 89 | 90 | bool Parser::at_end() const { 91 | return current == tokens.end(); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /lib/Parser/expression.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser.h" 2 | 3 | #include "BinaryExpression.h" 4 | #include "CallExpression.h" 5 | #include "DotExpression.h" 6 | #include "IdentifierExpression.h" 7 | #include "ListExpression.h" 8 | #include "MapExpression.h" 9 | #include "Operator.h" 10 | #include "Operator.h" 11 | #include "RetStatement.h" 12 | #include "StringValue.h" 13 | #include "SubscriptExpression.h" 14 | #include "UnaryExpression.h" 15 | #include "binary.h" 16 | #include "unary.h" 17 | 18 | using namespace std; 19 | 20 | namespace hap { 21 | 22 | struct not_an_expression : public runtime_error { 23 | not_an_expression() : runtime_error("not an expression") {} 24 | }; 25 | 26 | shared_ptr Parser::accept_expression 27 | (const shared_ptr environment) { 28 | try { 29 | stack operators; 30 | stack> operands; 31 | operators.push(Operator()); 32 | infix_expression(environment, operators, operands); 33 | return operands.top(); 34 | } catch (const not_an_expression&) { 35 | return nullptr; 36 | } 37 | } 38 | 39 | shared_ptr 40 | Parser::accept_value_expression(const shared_ptr environment) { 41 | auto value(first 42 | (environment, 43 | &Parser::accept_boolean_value, 44 | &Parser::accept_float_value, 45 | &Parser::accept_fun_value, 46 | &Parser::accept_integer_value, 47 | &Parser::accept_string_value, 48 | &Parser::accept_undefined_value, 49 | &Parser::accept_identifier_expression)); 50 | if (!value) 51 | return nullptr; 52 | return accept_suffix(environment, value); 53 | } 54 | 55 | shared_ptr Parser::accept_suffix 56 | (const shared_ptr environment, 57 | shared_ptr current) { 58 | decltype(current) previous; 59 | do { 60 | previous = current; 61 | current = accept_dot_suffix(environment, current); 62 | current = accept_call_suffix(environment, current); 63 | current = accept_subscript_suffix(environment, current); 64 | } while (current != previous); 65 | return current; 66 | } 67 | 68 | shared_ptr Parser::accept_dot_suffix 69 | (const shared_ptr environment, 70 | shared_ptr value) { 71 | if (!accept(Token::DOT)) 72 | return value; 73 | Token token; 74 | if (!accept(Token::IDENTIFIER, token)) 75 | expected("identifier"); 76 | return make_shared(value, token.string); 77 | } 78 | 79 | shared_ptr Parser::accept_call_suffix 80 | (const shared_ptr environment, 81 | shared_ptr value) { 82 | if (!accept(Token::LEFT_PARENTHESIS)) 83 | return value; 84 | vector> arguments; 85 | if (!peek(Token::RIGHT_PARENTHESIS)) { 86 | while (true) { 87 | if (peek(Token::RIGHT_PARENTHESIS)) 88 | break; 89 | auto argument(accept_expression(environment)); 90 | if (!argument) 91 | expected("expression"); 92 | arguments.push_back(argument); 93 | if (!accept(Token::COMMA)) 94 | break; 95 | } 96 | } 97 | expect(Token::RIGHT_PARENTHESIS); 98 | return make_shared(value, move(arguments)); 99 | } 100 | 101 | shared_ptr 102 | Parser::accept_subscript_suffix 103 | (const shared_ptr environment, 104 | shared_ptr value) { 105 | if (!accept(Token::LEFT_BRACKET)) 106 | return value; 107 | const auto index(accept_expression(environment)); 108 | if (!index) 109 | expected("expression"); 110 | expect(Token::RIGHT_BRACKET); 111 | return make_shared(value, index); 112 | } 113 | 114 | shared_ptr 115 | Parser::accept_identifier_expression(const shared_ptr) { 116 | Token token; 117 | if (!accept(Token::IDENTIFIER, token)) 118 | return nullptr; 119 | return make_shared(token.string); 120 | } 121 | 122 | bool Parser::accept_binary_operator(Operator& result) { 123 | if (at_end() || !(current->type == Token::OPERATOR 124 | || current->type == Token::IDENTIFIER)) 125 | return false; 126 | map::const_iterator operator_ 127 | (binary::operators.find(current->string)); 128 | if (operator_ == binary::operators.end()) 129 | return false; 130 | ++current; 131 | result = operator_->second; 132 | return true; 133 | } 134 | 135 | bool Parser::accept_unary_operator(Operator& result) { 136 | if (at_end() || !(current->type == Token::OPERATOR 137 | || current->type == Token::IDENTIFIER)) 138 | return false; 139 | map::const_iterator operator_ 140 | (unary::operators.find(current->string)); 141 | if (operator_ == unary::operators.end()) 142 | return false; 143 | ++current; 144 | result = operator_->second; 145 | return true; 146 | } 147 | 148 | void Parser::infix_expression 149 | (const shared_ptr environment, 150 | stack& operators, 151 | stack>& operands) { 152 | infix_subexpression(environment, operators, operands); 153 | Operator operator_; 154 | while (accept_binary_operator(operator_)) { 155 | push_operator(operator_, operators, operands); 156 | infix_subexpression(environment, operators, operands); 157 | } 158 | while (operators.top().arity != Operator::SENTINEL) 159 | pop_operator(operators, operands); 160 | } 161 | 162 | void Parser::infix_subexpression 163 | (const shared_ptr environment, 164 | stack& operators, 165 | stack>& operands) { 166 | Operator unary; 167 | shared_ptr value; 168 | if (accept(Token::LEFT_PARENTHESIS)) { 169 | operators.push(Operator()); 170 | infix_expression(environment, operators, operands); 171 | expect(Token::RIGHT_PARENTHESIS); 172 | operators.pop(); 173 | auto value(operands.top()); 174 | operands.pop(); 175 | operands.push(accept_suffix(environment, value)); 176 | } else if (accept(Token::LEFT_BRACKET)) { 177 | const auto list = make_shared(); 178 | operators.push(Operator()); 179 | if (!peek(Token::RIGHT_BRACKET)) { 180 | while (true) { 181 | if (peek(Token::RIGHT_BRACKET)) 182 | break; 183 | infix_expression(environment, operators, operands); 184 | list->push(operands.top()); 185 | operands.pop(); 186 | if (!accept(Token::COMMA)) 187 | break; 188 | } 189 | } 190 | expect(Token::RIGHT_BRACKET); 191 | operators.pop(); 192 | operands.push(static_pointer_cast(list)); 193 | } else if (accept(Token::LEFT_BRACE)) { 194 | const auto map = make_shared(); 195 | operators.push(Operator()); 196 | if (!peek(Token::RIGHT_BRACE)) { 197 | while (true) { 198 | if (peek(Token::RIGHT_BRACE)) 199 | break; 200 | shared_ptr key; 201 | Token bareword; 202 | if (accept(Token::IDENTIFIER, bareword)) { 203 | key.reset(new StringValue(bareword.string)); 204 | } else { 205 | infix_expression(environment, operators, operands); 206 | key = operands.top(); 207 | operands.pop(); 208 | } 209 | expect(Token::COLON); 210 | infix_expression(environment, operators, operands); 211 | shared_ptr value(operands.top()); 212 | operands.pop(); 213 | map->insert(key, value); 214 | if (!accept(Token::COMMA)) 215 | break; 216 | } 217 | } 218 | expect(Token::RIGHT_BRACE); 219 | operators.pop(); 220 | operands.push(static_pointer_cast(map)); 221 | } else if (accept_unary_operator(unary)) { 222 | push_operator(unary, operators, operands); 223 | infix_subexpression(environment, operators, operands); 224 | } else if ((value = accept_value_expression(environment))) { 225 | operands.push(value); 226 | } else { 227 | throw not_an_expression(); 228 | } 229 | } 230 | 231 | void Parser::pop_operator 232 | (stack& operators, 233 | stack>& operands) { 234 | if (operators.top().arity == Operator::BINARY) { 235 | Operator operator_(operators.top()); 236 | operators.pop(); 237 | shared_ptr b(operands.top()); 238 | operands.pop(); 239 | shared_ptr a(operands.top()); 240 | operands.pop(); 241 | operands.push(make_shared(operator_, a, b)); 242 | } else if (operators.top().arity == Operator::UNARY) { 243 | Operator operator_(operators.top()); 244 | operators.pop(); 245 | shared_ptr a(operands.top()); 246 | operands.pop(); 247 | operands.push(make_shared(operator_, a)); 248 | } else { 249 | throw runtime_error("error parsing expression"); 250 | } 251 | } 252 | 253 | void Parser::push_operator 254 | (const Operator& operator_, 255 | stack& operators, 256 | stack>& operands) { 257 | while (operator_ < operators.top()) 258 | pop_operator(operators, operands); 259 | operators.push(operator_); 260 | } 261 | 262 | } 263 | -------------------------------------------------------------------------------- /lib/Parser/statement.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser.h" 2 | 3 | #include "AtomicStatement.h" 4 | #include "BlockStatement.h" 5 | #include "ControlStatement.h" 6 | #include "DelStatement.h" 7 | #include "Expression.h" 8 | #include "ExpressionStatement.h" 9 | #include "FlowStatement.h" 10 | #include "ForStatement.h" 11 | #include "FunStatement.h" 12 | #include "FunValue.h" 13 | #include "RetStatement.h" 14 | #include "TraceStatement.h" 15 | #include "UndefinedValue.h" 16 | #include "VarStatement.h" 17 | 18 | using namespace std; 19 | 20 | namespace hap { 21 | 22 | shared_ptr 23 | Parser::accept_statements(const shared_ptr environment) { 24 | const auto block = make_shared(); 25 | shared_ptr statement; 26 | const auto local = make_shared(environment); 27 | while ((statement = accept_statement(local))) 28 | block->push(statement); 29 | return static_pointer_cast(block); 30 | } 31 | 32 | shared_ptr 33 | Parser::accept_statement(const shared_ptr environment) { 34 | return first 35 | (environment, 36 | &Parser::accept_atomic_statement, 37 | &Parser::accept_block_statement, 38 | &Parser::accept_del_statement, 39 | &Parser::accept_empty_statement, 40 | &Parser::accept_exit_statement, 41 | &Parser::accept_for_statement, 42 | &Parser::accept_fun_statement, 43 | &Parser::accept_if_statement, 44 | &Parser::accept_last_statement, 45 | &Parser::accept_next_statement, 46 | &Parser::accept_redo_statement, 47 | &Parser::accept_ret_statement, 48 | &Parser::accept_trace_statement, 49 | &Parser::accept_var_statement, 50 | &Parser::accept_when_statement, 51 | &Parser::accept_whenever_statement, 52 | &Parser::accept_while_statement, 53 | // ---- 54 | &Parser::accept_expression_statement); 55 | } 56 | 57 | shared_ptr 58 | Parser::accept_atomic_statement(const shared_ptr environment) { 59 | if (!accept(Token(Token::IDENTIFIER, "atomic"))) 60 | return nullptr; 61 | auto statement(accept_statement(environment)); 62 | if (!statement) 63 | expected("statement"); 64 | return make_shared(statement); 65 | } 66 | 67 | shared_ptr 68 | Parser::accept_block_statement(const shared_ptr environment) { 69 | if (!accept(Token::LEFT_BRACE)) 70 | return nullptr; 71 | shared_ptr block(accept_statements(environment)); 72 | expect(Token::RIGHT_BRACE); 73 | return block; 74 | } 75 | 76 | shared_ptr 77 | Parser::accept_del_statement(const shared_ptr environment) { 78 | if (!accept(Token(Token::IDENTIFIER, "del"))) 79 | return nullptr; 80 | Token identifier; 81 | expect(Token::IDENTIFIER, identifier); 82 | expect(Token::SEMICOLON); 83 | return make_shared(identifier.string); 84 | } 85 | 86 | shared_ptr 87 | Parser::accept_empty_statement(const shared_ptr) { 88 | return accept(Token::SEMICOLON) ? make_shared() : nullptr; 89 | } 90 | 91 | shared_ptr 92 | Parser::accept_exit_statement(const shared_ptr environment) { 93 | return accept_control_statement(environment, "exit"); 94 | } 95 | 96 | shared_ptr 97 | Parser::accept_expression_statement 98 | (const shared_ptr environment) { 99 | auto expression(accept_expression(environment)); 100 | if (!expression) 101 | return nullptr; 102 | expect(Token::SEMICOLON); 103 | return make_shared(expression); 104 | } 105 | 106 | shared_ptr Parser::accept_for_statement 107 | (const shared_ptr environment) { 108 | if (!accept(Token(Token::IDENTIFIER, "for"))) 109 | return nullptr; 110 | expect(Token::LEFT_PARENTHESIS); 111 | const auto initializer(accept_statement(environment)); 112 | if (!initializer) 113 | expected("for loop initializer"); 114 | const auto condition(accept_expression(environment)); 115 | if (!condition) 116 | expected("for loop condition"); 117 | expect(Token::SEMICOLON); 118 | const auto step(accept_expression(environment)); 119 | if (!step) 120 | expected("for loop step"); 121 | expect(Token::RIGHT_PARENTHESIS); 122 | const auto body(accept_statement(environment)); 123 | if (!body) 124 | expected("statement"); 125 | return make_shared(initializer, condition, step, body); 126 | } 127 | 128 | shared_ptr 129 | Parser::accept_fun_statement(const shared_ptr environment) { 130 | if (!accept(Token(Token::IDENTIFIER, "fun"))) 131 | return nullptr; 132 | Token identifier; 133 | expect(Token::IDENTIFIER, identifier); 134 | vector parameters; 135 | expect(Token::LEFT_PARENTHESIS); 136 | if (!peek(Token::RIGHT_PARENTHESIS)) { 137 | while (true) { 138 | if (peek(Token::RIGHT_PARENTHESIS)) 139 | break; 140 | Token parameter; 141 | expect(Token::IDENTIFIER, parameter); 142 | parameters.push_back(move(parameter.string)); 143 | if (!accept(Token::COMMA)) 144 | break; 145 | } 146 | } 147 | expect(Token::RIGHT_PARENTHESIS); 148 | shared_ptr body(accept_statement(environment)); 149 | if (!body) 150 | expected("statement"); 151 | return make_shared 152 | (identifier.string, parameters, body, environment); 153 | } 154 | 155 | shared_ptr 156 | Parser::accept_if_statement(const shared_ptr environment) { 157 | return accept_flow_statement(environment, "if"); 158 | } 159 | 160 | shared_ptr 161 | Parser::accept_last_statement(const shared_ptr environment) { 162 | return accept_control_statement(environment, "last"); 163 | } 164 | 165 | shared_ptr 166 | Parser::accept_next_statement(const shared_ptr environment) { 167 | return accept_control_statement(environment, "next"); 168 | } 169 | 170 | shared_ptr 171 | Parser::accept_redo_statement(const shared_ptr environment) { 172 | return accept_control_statement(environment, "redo"); 173 | } 174 | 175 | shared_ptr 176 | Parser::accept_ret_statement(const shared_ptr environment) { 177 | if (!accept(Token(Token::IDENTIFIER, "ret"))) 178 | return nullptr; 179 | auto expression(accept_expression(environment)); 180 | expect(Token::SEMICOLON); 181 | if (!expression) 182 | return make_shared 183 | (make_shared()); 184 | return make_shared(expression); 185 | } 186 | 187 | shared_ptr 188 | Parser::accept_trace_statement(const shared_ptr environment) { 189 | if (!accept(Token(Token::IDENTIFIER, "trace"))) 190 | return nullptr; 191 | auto expression(accept_expression(environment)); 192 | return expression ? make_shared(expression) : nullptr; 193 | } 194 | 195 | shared_ptr 196 | Parser::accept_var_statement(const shared_ptr environment) { 197 | if (!accept(Token(Token::IDENTIFIER, "var"))) 198 | return nullptr; 199 | Token identifier; 200 | expect(Token::IDENTIFIER, identifier); 201 | shared_ptr initializer = make_shared(); 202 | if (accept(Token(Token::OPERATOR, "=")) 203 | && !(initializer = accept_expression(environment))) 204 | expected("initializer"); 205 | expect(Token::SEMICOLON); 206 | return make_shared(identifier.string, initializer); 207 | } 208 | 209 | shared_ptr 210 | Parser::accept_when_statement(const shared_ptr environment) { 211 | return accept_flow_statement(environment, "when"); 212 | } 213 | 214 | shared_ptr 215 | Parser::accept_whenever_statement(const shared_ptr environment) { 216 | return accept_flow_statement(environment, "whenever"); 217 | } 218 | 219 | shared_ptr 220 | Parser::accept_while_statement(const shared_ptr environment) { 221 | return accept_flow_statement(environment, "while"); 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /lib/Parser/value.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser.h" 2 | 3 | #include "BooleanValue.h" 4 | #include "FloatValue.h" 5 | #include "FunValue.h" 6 | #include "IntegerValue.h" 7 | #include "RetStatement.h" 8 | #include "StringValue.h" 9 | #include "UndefinedValue.h" 10 | 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | namespace hap { 17 | 18 | shared_ptr Parser::accept_boolean_value 19 | (const shared_ptr) { 20 | if (accept(Token(Token::IDENTIFIER, "true"))) 21 | return make_shared(true); 22 | if (accept(Token(Token::IDENTIFIER, "false"))) 23 | return make_shared(false); 24 | return nullptr; 25 | } 26 | 27 | shared_ptr Parser::accept_float_value 28 | (const shared_ptr environment) { 29 | Token token; 30 | if (!accept(Token::FLOAT, token)) 31 | return nullptr; 32 | istringstream stream(token.string); 33 | double value = 0; 34 | if (!(stream >> value)) 35 | throw runtime_error("invalid float"); 36 | return make_shared(value); 37 | } 38 | 39 | shared_ptr Parser::accept_fun_value 40 | (const shared_ptr environment) { 41 | if (!accept(Token(Token::IDENTIFIER, "lam"))) 42 | return nullptr; 43 | Token identifier(Token::IDENTIFIER, "lambda"); 44 | accept(Token::IDENTIFIER, identifier); 45 | vector parameters; 46 | expect(Token::LEFT_PARENTHESIS); 47 | if (!peek(Token::RIGHT_PARENTHESIS)) { 48 | while (true) { 49 | if (peek(Token::RIGHT_PARENTHESIS)) 50 | break; 51 | Token parameter; 52 | expect(Token::IDENTIFIER, parameter); 53 | parameters.push_back(parameter.string); 54 | if (!accept(Token::COMMA)) 55 | break; 56 | } 57 | } 58 | expect(Token::RIGHT_PARENTHESIS); 59 | shared_ptr body; 60 | if (accept(Token::COLON)) { 61 | auto expression(accept_expression(environment)); 62 | if (!expression) 63 | expected("expression"); 64 | body.reset(new RetStatement(expression)); 65 | } else if (accept(Token::LEFT_BRACE)) { 66 | body = accept_statements(environment); 67 | expect(Token::RIGHT_BRACE); 68 | } else { 69 | expected("colon or block"); 70 | } 71 | return make_shared 72 | (identifier.string, parameters, body, environment); 73 | } 74 | 75 | shared_ptr Parser::accept_integer_value 76 | (const shared_ptr environment) { 77 | Token token; 78 | if (!accept(Token::INTEGER, token)) 79 | return nullptr; 80 | istringstream stream(token.string); 81 | int32_t value = 0; 82 | if (!(stream >> value)) 83 | throw runtime_error("invalid integer"); 84 | return make_shared(value); 85 | } 86 | 87 | shared_ptr Parser::accept_string_value 88 | (const shared_ptr) { 89 | Token token; 90 | if (!accept(Token::STRING, token)) 91 | return nullptr; 92 | token.string.pop_back(); 93 | return make_shared(token.string.substr(1)); 94 | } 95 | 96 | shared_ptr Parser::accept_undefined_value 97 | (const shared_ptr) { 98 | if (accept(Token(Token::IDENTIFIER, "undefined"))) 99 | return make_shared(); 100 | return nullptr; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /lib/Statement/AtomicStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "AtomicStatement.h" 2 | 3 | #include "Atomic.h" 4 | 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace hap { 10 | 11 | AtomicStatement::AtomicStatement 12 | (const shared_ptr statement) 13 | : statement(statement) {} 14 | 15 | bool AtomicStatement::equal(const Statement& statement) const { 16 | if (const auto that 17 | = dynamic_cast(&statement)) { 18 | return *this->statement == *that->statement; 19 | } 20 | return false; 21 | } 22 | 23 | void AtomicStatement::exec 24 | (Context& context, 25 | const shared_ptr environment) const { 26 | Atomic atomic(context); 27 | statement->execute(context, environment); 28 | } 29 | 30 | void AtomicStatement::write(ostream& stream) const { 31 | stream << "atomic " << *statement; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /lib/Statement/AtomicStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_ATOMICSTATEMENT_H 2 | #define HAP_ATOMICSTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | namespace hap { 7 | 8 | class AtomicStatement : public Statement { 9 | public: 10 | AtomicStatement(std::shared_ptr); 11 | protected: 12 | virtual bool equal(const Statement&) const; 13 | virtual void exec 14 | (Context&, std::shared_ptr) const final override; 15 | virtual void write(std::ostream&) const final override; 16 | private: 17 | std::shared_ptr statement; 18 | }; 19 | 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /lib/Statement/BlockStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "BlockStatement.h" 2 | 3 | #include "Value.h" 4 | #include "UndefinedValue.h" 5 | #include "indirect_compare.h" 6 | 7 | #include 8 | 9 | using namespace std; 10 | 11 | namespace hap { 12 | 13 | BlockStatement::BlockStatement 14 | (std::initializer_list statements) { 15 | for (const auto& statement : statements) 16 | push(shared_ptr(statement)); 17 | } 18 | 19 | bool BlockStatement::equal(const Statement& statement) const { 20 | if (const auto that 21 | = dynamic_cast(&statement)) { 22 | return statements.size() == that->statements.size() 23 | && std::equal 24 | (begin(statements), end(statements), 25 | begin(that->statements), indirect_equal()); 26 | } 27 | return false; 28 | } 29 | 30 | void BlockStatement::exec 31 | (Context& context, const shared_ptr environment) const { 32 | for (const auto& statement : statements) 33 | statement->execute(context, environment); 34 | } 35 | 36 | void BlockStatement::write(ostream& stream) const { 37 | stream << "{\n"; 38 | for (const auto& statement : statements) 39 | stream << *statement; 40 | stream << "}\n"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /lib/Statement/BlockStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_BLOCKSTATEMENT_H 2 | #define HAP_BLOCKSTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class BlockStatement : public Statement { 11 | public: 12 | BlockStatement() {} 13 | BlockStatement(std::initializer_list); 14 | void push(std::shared_ptr statement) { 15 | statements.push_back(statement); 16 | } 17 | protected: 18 | virtual bool equal(const Statement&) const; 19 | virtual void exec 20 | (Context&, std::shared_ptr) const final override; 21 | virtual void write(std::ostream&) const final override; 22 | private: 23 | std::vector> statements; 24 | }; 25 | 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /lib/Statement/ControlStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "ControlStatement.h" 2 | 3 | #include "flow.h" 4 | 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace hap { 10 | 11 | bool ControlStatement::equal(const Statement& statement) const { 12 | if (const auto that 13 | = dynamic_cast(&statement)) { 14 | return control == that->control; 15 | } 16 | return false; 17 | } 18 | 19 | void ControlStatement::write(ostream& stream) const { 20 | stream << control << ";\n"; 21 | } 22 | 23 | #define CONTROL_STATEMENT(NAME) \ 24 | void NAME##Statement::exec(Context&, const shared_ptr) const { \ 25 | throw flow::NAME(); \ 26 | } 27 | 28 | CONTROL_STATEMENT(Exit) 29 | CONTROL_STATEMENT(Next) 30 | CONTROL_STATEMENT(Last) 31 | CONTROL_STATEMENT(Redo) 32 | 33 | #undef CONTROL_STATEMENT 34 | 35 | ostream& operator<<(ostream& stream, ControlStatement::Control control) { 36 | switch (control) { 37 | case ControlStatement::EXIT: return stream << "exit"; 38 | case ControlStatement::NEXT: return stream << "next"; 39 | case ControlStatement::LAST: return stream << "last"; 40 | case ControlStatement::REDO: return stream << "redo"; 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /lib/Statement/ControlStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_CONTROLSTATEMENT_H 2 | #define HAP_CONTROLSTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class ControlStatement : public Statement { 11 | public: 12 | enum Control { 13 | EXIT, 14 | LAST, 15 | NEXT, 16 | REDO, 17 | }; 18 | ControlStatement(Control control) 19 | : control(control) {} 20 | protected: 21 | virtual bool equal(const Statement&) const; 22 | virtual void exec 23 | (Context&, std::shared_ptr) const = 0; 24 | virtual void write(std::ostream&) const final override; 25 | Control control; 26 | }; 27 | 28 | std::ostream& operator<<(std::ostream&, ControlStatement::Control); 29 | 30 | #define CONTROL_STATEMENT(NAME, KEYWORD) \ 31 | class NAME##Statement : public ControlStatement { \ 32 | public: \ 33 | NAME##Statement() : ControlStatement(KEYWORD) {} \ 34 | virtual void exec \ 35 | (Context&, std::shared_ptr) const final override; \ 36 | }; 37 | 38 | CONTROL_STATEMENT(Exit, EXIT) 39 | CONTROL_STATEMENT(Last, LAST) 40 | CONTROL_STATEMENT(Next, NEXT) 41 | CONTROL_STATEMENT(Redo, REDO) 42 | 43 | #undef CONTROL_STATEMENT 44 | 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /lib/Statement/DelStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "DelStatement.h" 2 | 3 | #include "Environment.h" 4 | 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace hap { 10 | 11 | DelStatement::DelStatement(const std::string& identifier) 12 | : identifier(identifier) {} 13 | 14 | bool DelStatement::equal(const Statement& statement) const { 15 | if (const auto that 16 | = dynamic_cast(&statement)) { 17 | return identifier == that->identifier; 18 | } 19 | return false; 20 | } 21 | 22 | void DelStatement::exec 23 | (Context&, const shared_ptr environment) const { 24 | environment->undefine(identifier); 25 | } 26 | 27 | void DelStatement::write(ostream& stream) const { 28 | stream << "del " << identifier << ";\n"; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /lib/Statement/DelStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_DELSTATEMENT_H 2 | #define HAP_DELSTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class DelStatement : public Statement { 11 | public: 12 | DelStatement(const std::string&); 13 | protected: 14 | virtual bool equal(const Statement&) const; 15 | virtual void exec 16 | (Context&, std::shared_ptr) const final override; 17 | virtual void write(std::ostream&) const final override; 18 | private: 19 | std::string identifier; 20 | }; 21 | 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /lib/Statement/ExpressionStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "ExpressionStatement.h" 2 | #include "Expression.h" 3 | #include "Value.h" 4 | 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace hap { 10 | 11 | ExpressionStatement::ExpressionStatement 12 | (Expression* const expression) 13 | : expression(expression) {} 14 | 15 | ExpressionStatement::ExpressionStatement 16 | (shared_ptr expression) 17 | : expression(expression) {} 18 | 19 | bool ExpressionStatement::equal(const Statement& statement) const { 20 | if (const auto that 21 | = dynamic_cast(&statement)) { 22 | return *expression == *that->expression; 23 | } 24 | return false; 25 | } 26 | 27 | void ExpressionStatement::exec 28 | (Context& context, const shared_ptr environment) const { 29 | expression->eval(context, environment); 30 | } 31 | 32 | void ExpressionStatement::write(ostream& stream) const { 33 | stream << *expression << ";\n"; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /lib/Statement/ExpressionStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_EXPRESSIONSTATEMENT_H 2 | #define HAP_EXPRESSIONSTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | namespace hap { 7 | 8 | class ExpressionStatement : public Statement { 9 | public: 10 | ExpressionStatement(Expression*); 11 | ExpressionStatement(std::shared_ptr); 12 | protected: 13 | virtual bool equal(const Statement&) const; 14 | virtual void exec 15 | (Context&, std::shared_ptr) const final override; 16 | virtual void write(std::ostream&) const final override; 17 | private: 18 | std::shared_ptr expression; 19 | }; 20 | 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /lib/Statement/FlowStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "FlowStatement.h" 2 | 3 | #include "BooleanValue.h" 4 | #include "Context.h" 5 | #include "UndefinedValue.h" 6 | #include "Value.h" 7 | #include "flow.h" 8 | 9 | #include 10 | 11 | using namespace std; 12 | 13 | namespace hap { 14 | 15 | FlowStatement::FlowStatement 16 | (const string& keyword, 17 | Expression* expression, 18 | Statement* statement) 19 | : keyword(keyword), 20 | expression(expression), 21 | statement(statement) {} 22 | 23 | FlowStatement::FlowStatement 24 | (const string& keyword, 25 | const shared_ptr expression, 26 | const shared_ptr statement) 27 | : keyword(keyword), 28 | expression(expression), 29 | statement(statement) {} 30 | 31 | bool FlowStatement::equal(const Statement& statement) const { 32 | if (const auto that 33 | = dynamic_cast(&statement)) { 34 | return keyword == that->keyword 35 | && *expression == *that->expression 36 | && *this->statement == *that->statement; 37 | } 38 | return false; 39 | } 40 | 41 | void FlowStatement::write(ostream& stream) const { 42 | stream << keyword << " (" << *expression << ")\n" << *statement; 43 | } 44 | 45 | #define FLOW_STATEMENT(NAME, KEYWORD) \ 46 | NAME##Statement::NAME##Statement \ 47 | (Expression* const expression, \ 48 | Statement* const statement) \ 49 | : FlowStatement(KEYWORD, expression, statement) {} \ 50 | NAME##Statement::NAME##Statement \ 51 | (const shared_ptr expression, \ 52 | const shared_ptr statement) \ 53 | : FlowStatement(KEYWORD, expression, statement) {} 54 | 55 | FLOW_STATEMENT(If, "if") 56 | FLOW_STATEMENT(When, "when") 57 | FLOW_STATEMENT(Whenever, "whenever") 58 | FLOW_STATEMENT(While, "while") 59 | 60 | #undef FLOW_STATEMENT 61 | 62 | void IfStatement::exec 63 | (Context& context, const shared_ptr environment) const { 64 | if (eval_as(expression, context, environment)->value) 65 | statement->execute(context, environment); 66 | } 67 | 68 | void WhenStatement::exec 69 | (Context& context, const shared_ptr environment) const { 70 | context.listen(expression, Context::ONCE, statement, environment); 71 | } 72 | 73 | void WheneverStatement::exec 74 | (Context& context, const shared_ptr environment) const { 75 | context.listen(expression, Context::REPEATEDLY, statement, environment); 76 | } 77 | 78 | void WhileStatement::exec 79 | (Context& context, const shared_ptr environment) const { 80 | condition: 81 | if (!eval_as(expression, context, environment)->value) 82 | goto end; 83 | 84 | body: 85 | try { 86 | statement->execute(context, environment); 87 | } catch (const flow::Last&) { 88 | goto end; 89 | } catch (const flow::Next&) { 90 | } catch (const flow::Redo&) { 91 | goto body; 92 | } 93 | goto condition; 94 | 95 | end: 96 | return; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /lib/Statement/FlowStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_FLOWSTATEMENT_H 2 | #define HAP_FLOWSTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class FlowStatement : public Statement { 11 | public: 12 | FlowStatement(const std::string&, Expression*, Statement*); 13 | FlowStatement 14 | (const std::string&, 15 | std::shared_ptr, 16 | std::shared_ptr); 17 | protected: 18 | virtual bool equal(const Statement&) const; 19 | virtual void exec 20 | (Context&, std::shared_ptr) const = 0; 21 | virtual void write(std::ostream&) const final override; 22 | std::string keyword; 23 | std::shared_ptr expression; 24 | std::shared_ptr statement; 25 | }; 26 | 27 | #define FLOW_STATEMENT(NAME, KEYWORD) \ 28 | class NAME##Statement : public FlowStatement { \ 29 | public: \ 30 | NAME##Statement(Expression*, Statement*); \ 31 | NAME##Statement \ 32 | (std::shared_ptr, \ 33 | std::shared_ptr); \ 34 | virtual void exec \ 35 | (Context&, std::shared_ptr) const final override; \ 36 | }; 37 | 38 | FLOW_STATEMENT(If, "if") 39 | FLOW_STATEMENT(When, "when") 40 | FLOW_STATEMENT(Whenever, "whenever") 41 | FLOW_STATEMENT(While, "while") 42 | 43 | #undef FLOW_STATEMENT 44 | 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /lib/Statement/ForStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "ForStatement.h" 2 | 3 | #include "BooleanValue.h" 4 | #include "Environment.h" 5 | #include "flow.h" 6 | 7 | #include 8 | 9 | using namespace std; 10 | 11 | namespace hap { 12 | 13 | ForStatement::ForStatement 14 | (Statement* const initializer, 15 | Expression* const condition, 16 | Expression* const step, 17 | Statement* const body) 18 | : initializer(initializer), 19 | condition(condition), 20 | step(step), 21 | body(body) {} 22 | 23 | ForStatement::ForStatement 24 | (const shared_ptr initializer, 25 | const shared_ptr condition, 26 | const shared_ptr step, 27 | const shared_ptr body) 28 | : initializer(initializer), 29 | condition(condition), 30 | step(step), 31 | body(body) {} 32 | 33 | bool ForStatement::equal(const Statement& statement) const { 34 | if (const auto that 35 | = dynamic_cast(&statement)) { 36 | return *initializer == *that->initializer 37 | && *condition == *that->condition 38 | && *step == *that->step 39 | && *body == *that->body; 40 | } 41 | return false; 42 | } 43 | 44 | void ForStatement::exec 45 | (Context& context, const shared_ptr environment) const { 46 | const auto local = make_shared(environment); 47 | initializer->execute(context, local); 48 | 49 | condition: 50 | if (!eval_as(condition, context, local)->value) 51 | goto end; 52 | 53 | body: 54 | try { 55 | body->execute(context, local); 56 | } catch (const flow::Last&) { 57 | goto end; 58 | } catch (const flow::Next&) { 59 | } catch (const flow::Redo&) { 60 | goto body; 61 | } 62 | step->eval(context, local); 63 | goto condition; 64 | 65 | end: 66 | return; 67 | } 68 | 69 | void ForStatement::write(ostream& stream) const { 70 | stream 71 | << "for (" 72 | << *initializer << "; " 73 | << *condition << "; " 74 | << *step << ")\n" 75 | << *body; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /lib/Statement/ForStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_FORSTATEMENT_H 2 | #define HAP_FORSTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | namespace hap { 7 | 8 | class ForStatement : public Statement { 9 | public: 10 | ForStatement(Statement*, Expression*, Expression*, Statement*); 11 | ForStatement 12 | (std::shared_ptr, 13 | std::shared_ptr, 14 | std::shared_ptr, 15 | std::shared_ptr); 16 | protected: 17 | virtual bool equal(const Statement&) const; 18 | virtual void exec 19 | (Context&, std::shared_ptr) const final override; 20 | virtual void write(std::ostream&) const final override; 21 | private: 22 | std::shared_ptr initializer; 23 | std::shared_ptr condition; 24 | std::shared_ptr step; 25 | std::shared_ptr body; 26 | }; 27 | 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /lib/Statement/FunStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "FunStatement.h" 2 | 3 | #include "FunValue.h" 4 | #include "UndefinedValue.h" 5 | 6 | #include 7 | 8 | using namespace std; 9 | 10 | namespace hap { 11 | 12 | FunStatement::FunStatement 13 | (const std::string& identifier, 14 | std::initializer_list parameters, 15 | Statement* const body) 16 | : identifier(identifier), 17 | parameters(parameters), 18 | body(body) {} 19 | 20 | FunStatement::FunStatement 21 | (const string& identifier, 22 | const vector& parameters, 23 | const shared_ptr body, 24 | const shared_ptr environment) 25 | : identifier(identifier), 26 | parameters(parameters), 27 | body(body), 28 | local(environment) {} 29 | 30 | bool FunStatement::equal(const Statement& statement) const { 31 | if (const auto that 32 | = dynamic_cast(&statement)) { 33 | return identifier == that->identifier 34 | && parameters == that->parameters 35 | && *body == *that->body; 36 | } 37 | return false; 38 | } 39 | 40 | void FunStatement::exec 41 | (Context&, const shared_ptr environment) const { 42 | environment->define 43 | (identifier, make_shared(identifier, parameters, body, local)); 44 | } 45 | 46 | void FunStatement::write(ostream& stream) const { 47 | stream << "fun " << identifier << "("; 48 | for (const auto& parameter : parameters) 49 | stream << parameter << ", "; 50 | stream << ") " << *body; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /lib/Statement/FunStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_FUNSTATEMENT_H 2 | #define HAP_FUNSTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | #include "Environment.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace hap { 12 | 13 | class FunStatement : public Statement { 14 | public: 15 | FunStatement 16 | (const std::string&, 17 | std::initializer_list, 18 | Statement*); 19 | FunStatement 20 | (const std::string&, 21 | const std::vector&, 22 | std::shared_ptr, 23 | std::shared_ptr); 24 | protected: 25 | virtual bool equal(const Statement&) const; 26 | virtual void exec 27 | (Context&, std::shared_ptr) const final override; 28 | virtual void write(std::ostream&) const final override; 29 | private: 30 | std::string identifier; 31 | std::vector parameters; 32 | std::shared_ptr body; 33 | std::shared_ptr local; 34 | }; 35 | 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /lib/Statement/RetStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "RetStatement.h" 2 | #include "Expression.h" 3 | #include "flow.h" 4 | 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace hap { 10 | 11 | RetStatement::RetStatement(Expression* const expression) 12 | : expression(expression) {} 13 | 14 | RetStatement::RetStatement(const shared_ptr expression) 15 | : expression(expression) {} 16 | 17 | bool RetStatement::equal(const Statement& statement) const { 18 | if (const auto that 19 | = dynamic_cast(&statement)) { 20 | return *expression == *that->expression; 21 | } 22 | return false; 23 | } 24 | 25 | void RetStatement::exec 26 | (Context& context, const shared_ptr environment) const { 27 | auto value(expression->eval(context, environment)); 28 | throw flow::Return(value); 29 | } 30 | 31 | void RetStatement::write(ostream& stream) const { 32 | stream << "ret " << *expression << ";\n"; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /lib/Statement/RetStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_RETSTATEMENT_H 2 | #define HAP_RETSTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | namespace hap { 7 | 8 | class RetStatement : public Statement { 9 | public: 10 | RetStatement(Expression*); 11 | RetStatement(std::shared_ptr); 12 | protected: 13 | virtual bool equal(const Statement&) const; 14 | virtual void exec 15 | (Context&, std::shared_ptr) const final override; 16 | virtual void write(std::ostream&) const final override; 17 | private: 18 | std::shared_ptr expression; 19 | }; 20 | 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /lib/Statement/Statement.cpp: -------------------------------------------------------------------------------- 1 | #include "Statement.h" 2 | 3 | #include "Context.h" 4 | #include "Environment.h" 5 | #include "Expression.h" 6 | #include "flow.h" 7 | 8 | #include 9 | 10 | using namespace std; 11 | 12 | namespace hap { 13 | 14 | Statement::~Statement() {} 15 | 16 | void Statement::execute 17 | (Context& context, 18 | const std::shared_ptr environment) const { 19 | exec(context, environment); 20 | context.interrupt(environment); 21 | } 22 | 23 | bool operator==(const Statement& a, const Statement& b) { 24 | return a.equal(b); 25 | } 26 | 27 | ostream& operator<<(ostream& stream, const Statement& statement) { 28 | statement.write(stream); 29 | return stream; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lib/Statement/Statement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_STATEMENT_H 2 | #define HAP_STATEMENT_H 3 | 4 | #include 5 | 6 | namespace hap { 7 | 8 | class Context; 9 | class Environment; 10 | class Expression; 11 | class Value; 12 | 13 | class Statement { 14 | public: 15 | virtual ~Statement(); 16 | void execute(Context&, std::shared_ptr) const; 17 | friend bool operator==(const Statement&, const Statement&); 18 | friend std::ostream& operator<<(std::ostream&, const Statement&); 19 | protected: 20 | Statement() {} 21 | virtual bool equal(const Statement&) const = 0; 22 | virtual void exec(Context&, std::shared_ptr) const = 0; 23 | virtual void write(std::ostream&) const = 0; 24 | private: 25 | Statement(const Statement&); 26 | Statement& operator=(const Statement&); 27 | }; 28 | 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /lib/Statement/TraceStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "TraceStatement.h" 2 | #include "Value.h" 3 | 4 | #include 5 | 6 | using namespace std; 7 | 8 | namespace hap { 9 | 10 | TraceStatement::TraceStatement(shared_ptr expression) 11 | : expression(expression) {} 12 | 13 | bool TraceStatement::equal(const Statement& statement) const { 14 | if (const auto that 15 | = dynamic_cast(&statement)) { 16 | return *expression == *that->expression; 17 | } 18 | return false; 19 | } 20 | 21 | void TraceStatement::exec 22 | (Context& context, const shared_ptr environment) const { 23 | auto value(expression->eval(context, environment)); 24 | cout << *value << endl; 25 | } 26 | 27 | void TraceStatement::write(ostream& stream) const { 28 | stream << "trace " << *expression << ";\n"; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /lib/Statement/TraceStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_TRACESTATEMENT_H 2 | #define HAP_TRACESTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | namespace hap { 7 | 8 | class TraceStatement : public Statement { 9 | public: 10 | TraceStatement(std::shared_ptr); 11 | protected: 12 | virtual bool equal(const Statement&) const; 13 | virtual void exec 14 | (Context&, std::shared_ptr) const final override; 15 | virtual void write(std::ostream&) const final override; 16 | private: 17 | std::shared_ptr expression; 18 | }; 19 | 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /lib/Statement/VarStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "VarStatement.h" 2 | 3 | #include "UndefinedValue.h" 4 | #include "Environment.h" 5 | 6 | #include 7 | 8 | using namespace std; 9 | 10 | namespace hap { 11 | 12 | VarStatement::VarStatement 13 | (const string& identifier, 14 | Expression* const initializer) 15 | : identifier(identifier), 16 | initializer(initializer) {} 17 | 18 | VarStatement::VarStatement 19 | (const string& identifier, 20 | const shared_ptr initializer) 21 | : identifier(identifier), 22 | initializer(initializer) {} 23 | 24 | bool VarStatement::equal(const Statement& statement) const { 25 | if (const auto that 26 | = dynamic_cast(&statement)) { 27 | return identifier == that->identifier 28 | && *initializer == *that->initializer; 29 | } 30 | return false; 31 | } 32 | 33 | void VarStatement::exec 34 | (Context& context, const shared_ptr environment) const { 35 | auto value(initializer->eval(context, environment)); 36 | environment->define(identifier, value); 37 | } 38 | 39 | void VarStatement::write(ostream& stream) const { 40 | stream << "var " << identifier; 41 | if (initializer) 42 | stream << " = " << *initializer; 43 | stream << ";\n"; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /lib/Statement/VarStatement.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_VARSTATEMENT_H 2 | #define HAP_VARSTATEMENT_H 3 | 4 | #include "Statement.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class VarStatement : public Statement { 11 | public: 12 | VarStatement(const std::string&, Expression*); 13 | VarStatement(const std::string&, std::shared_ptr); 14 | protected: 15 | virtual bool equal(const Statement&) const; 16 | virtual void exec 17 | (Context&, std::shared_ptr) const final override; 18 | virtual void write(std::ostream&) const final override; 19 | private: 20 | std::string identifier; 21 | std::shared_ptr initializer; 22 | }; 23 | 24 | } 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /lib/Token.cpp: -------------------------------------------------------------------------------- 1 | #include "Token.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | namespace hap { 9 | 10 | ostream& operator<<(ostream& stream, const Token& token) { 11 | return stream << token.type << " '" << token.string << "'"; 12 | } 13 | 14 | ostream& operator<<(ostream& stream, const Token::Type& type) { 15 | switch (type) { 16 | case Token::UNDEFINED: throw runtime_error("undefined token"); 17 | case Token::DOT: return stream << "dot"; 18 | case Token::COLON: return stream << "colon"; 19 | case Token::COMMA: return stream << "comma"; 20 | case Token::IDENTIFIER: return stream << "identifier"; 21 | case Token::INTEGER: return stream << "integer"; 22 | case Token::FLOAT: return stream << "float"; 23 | case Token::LEFT_BRACE: return stream << "left brace"; 24 | case Token::LEFT_BRACKET: return stream << "left bracket"; 25 | case Token::LEFT_PARENTHESIS: return stream << "left parenthesis"; 26 | case Token::OPERATOR: return stream << "operator"; 27 | case Token::RIGHT_BRACE: return stream << "right brace"; 28 | case Token::RIGHT_BRACKET: return stream << "right bracket"; 29 | case Token::RIGHT_PARENTHESIS: return stream << "right parenthesis"; 30 | case Token::SEMICOLON: return stream << "semicolon"; 31 | case Token::STRING: return stream << "string"; 32 | } 33 | } 34 | 35 | bool operator==(const Token& a, const Token& b) { 36 | return a.type == b.type && a.string == b.string; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /lib/Token.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_TOKEN_H 2 | #define HAP_TOKEN_H 3 | 4 | #include 5 | #include 6 | 7 | namespace hap { 8 | 9 | struct Token { 10 | enum Type { 11 | UNDEFINED, 12 | DOT, 13 | COLON, 14 | COMMA, 15 | FLOAT, 16 | IDENTIFIER, 17 | INTEGER, 18 | LEFT_BRACE, 19 | LEFT_BRACKET, 20 | LEFT_PARENTHESIS, 21 | OPERATOR, 22 | RIGHT_BRACE, 23 | RIGHT_BRACKET, 24 | RIGHT_PARENTHESIS, 25 | SEMICOLON, 26 | STRING, 27 | }; 28 | Token() : type(UNDEFINED) {} 29 | Token(Type type, const std::string& string) 30 | : type(type), string(string) {} 31 | Type type; 32 | std::string string; 33 | friend std::ostream& operator<<(std::ostream&, const Token&); 34 | friend bool operator==(const Token&, const Token&); 35 | }; 36 | 37 | std::ostream& operator<<(std::ostream&, const Token::Type&); 38 | 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /lib/Value/BooleanValue.cpp: -------------------------------------------------------------------------------- 1 | #include "BooleanValue.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace hap { 8 | 9 | bool BooleanValue::equal(const Expression& expression) const { 10 | if (const auto that 11 | = dynamic_cast(&expression)) { 12 | return value == that->value; 13 | } 14 | return false; 15 | } 16 | 17 | shared_ptr BooleanValue::eval 18 | (Context&, const shared_ptr) const { 19 | return make_shared(*this); 20 | } 21 | 22 | bool BooleanValue::less(const Value& that) const { 23 | return Value::less(that) 24 | || (that.type() == Value::BOOLEAN 25 | && value < static_cast(that).value); 26 | } 27 | 28 | void BooleanValue::write(ostream& stream) const { 29 | stream << (value ? "true" : "false"); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lib/Value/BooleanValue.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_BOOLEANVALUE_H 2 | #define HAP_BOOLEANVALUE_H 3 | 4 | #include "Value.h" 5 | 6 | namespace hap { 7 | 8 | class BooleanValue : public Value { 9 | public: 10 | BooleanValue(bool value) 11 | : value(value) {} 12 | virtual Value::Type type() const final override { 13 | return BOOLEAN; 14 | } 15 | virtual BooleanValue* copy() const final override { 16 | return new BooleanValue(*this); 17 | } 18 | virtual std::shared_ptr eval 19 | (Context&, std::shared_ptr) const final override; 20 | virtual bool less(const Value&) const final override; 21 | bool value; 22 | protected: 23 | virtual bool equal(const Expression&) const final override; 24 | virtual void write(std::ostream&) const final override; 25 | }; 26 | 27 | template<> 28 | struct value_traits { 29 | typedef BooleanValue type; 30 | }; 31 | 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /lib/Value/FloatValue.cpp: -------------------------------------------------------------------------------- 1 | #include "FloatValue.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | namespace hap { 9 | 10 | bool FloatValue::equal(const Expression& expression) const { 11 | if (const auto that 12 | = dynamic_cast(&expression)) { 13 | return value == that->value; 14 | } 15 | return false; 16 | } 17 | 18 | shared_ptr FloatValue::eval 19 | (Context&, const shared_ptr) const { 20 | return make_shared(*this); 21 | } 22 | 23 | bool FloatValue::less(const Value& that) const { 24 | return Value::less(that) 25 | || (that.type() == Value::FLOAT 26 | && value < static_cast(that).value); 27 | } 28 | 29 | void FloatValue::write(ostream& stream) const { 30 | stream << setprecision(10) << showpoint << value; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /lib/Value/FloatValue.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_FLOATVALUE_H 2 | #define HAP_FLOATVALUE_H 3 | 4 | #include "Value.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class FloatValue : public Value { 11 | public: 12 | FloatValue(double value) 13 | : value(value) {} 14 | virtual Value::Type type() const final override { 15 | return FLOAT; 16 | } 17 | virtual FloatValue* copy() const final override { 18 | return new FloatValue(*this); 19 | } 20 | virtual std::shared_ptr eval 21 | (Context&, std::shared_ptr) const final override; 22 | virtual bool less(const Value&) const final override; 23 | double value; 24 | protected: 25 | virtual bool equal(const Expression&) const final override; 26 | virtual void write(std::ostream&) const final override; 27 | }; 28 | 29 | template<> 30 | struct value_traits { 31 | typedef FloatValue type; 32 | }; 33 | 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /lib/Value/FunValue.cpp: -------------------------------------------------------------------------------- 1 | #include "FunValue.h" 2 | 3 | #include "UndefinedValue.h" 4 | #include "flow.h" 5 | #include "indirect_compare.h" 6 | 7 | #include 8 | 9 | using namespace std; 10 | 11 | namespace hap { 12 | 13 | FunValue::FunValue 14 | (const std::string& identifier, 15 | std::initializer_list parameters, 16 | Statement* body) 17 | : identifier(identifier), 18 | parameters(parameters), 19 | body(body) {} 20 | 21 | FunValue::FunValue 22 | (const string& identifier, 23 | const vector& parameters, 24 | shared_ptr body, 25 | shared_ptr environment) 26 | : identifier(identifier), 27 | parameters(parameters), 28 | body(body), 29 | environment(environment) {} 30 | 31 | shared_ptr FunValue::eval 32 | (Context&, const shared_ptr environment) const { 33 | return make_shared(identifier, parameters, body, environment); 34 | } 35 | 36 | bool FunValue::less(const Value& that) const { 37 | return Value::less(that); 38 | } 39 | 40 | bool FunValue::equal(const Expression& expression) const { 41 | if (const auto that 42 | = dynamic_cast(&expression)) { 43 | return identifier == that->identifier 44 | && parameters == that->parameters 45 | && *body == *that->body; 46 | } 47 | return false; 48 | } 49 | 50 | void FunValue::write(ostream& stream) const { 51 | stream << "lam " << identifier << "("; 52 | for (const auto& parameter : parameters) 53 | stream << parameter << ", "; 54 | stream << ") " << *body; 55 | } 56 | 57 | shared_ptr FunValue::call 58 | (Context& context, const vector>& arguments) const { 59 | const auto local = make_shared(environment); 60 | auto parameter(parameters.begin()); 61 | for (const auto& argument : arguments) { 62 | auto value(argument->eval(context, environment)); 63 | local->define(*parameter++, value); 64 | } 65 | try { 66 | body->execute(context, local); 67 | } catch (flow::Return& result) { 68 | return result.value; 69 | } 70 | return make_shared(); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /lib/Value/FunValue.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_FUNEXPRESSION_H 2 | #define HAP_FUNEXPRESSION_H 3 | 4 | #include "Value.h" 5 | 6 | #include "Environment.h" 7 | #include "Statement.h" 8 | 9 | #include 10 | #include 11 | 12 | namespace hap { 13 | 14 | class FunValue : public Value { 15 | public: 16 | FunValue 17 | (const std::string&, 18 | std::initializer_list, 19 | Statement*); 20 | FunValue 21 | (const std::string&, 22 | const std::vector&, 23 | std::shared_ptr, 24 | std::shared_ptr); 25 | virtual Value::Type type() const final override { 26 | return FUNCTION; 27 | } 28 | virtual FunValue* copy() const final override { 29 | return new FunValue(*this); 30 | } 31 | virtual std::shared_ptr eval 32 | (Context&, std::shared_ptr) const final override; 33 | virtual bool less(const Value&) const final override; 34 | std::shared_ptr call 35 | (Context&, const std::vector>&) const; 36 | protected: 37 | virtual bool equal(const Expression&) const final override; 38 | virtual void write(std::ostream&) const final override; 39 | private: 40 | FunValue(const FunValue&) = default; 41 | std::string identifier; 42 | std::vector parameters; 43 | std::shared_ptr body; 44 | std::shared_ptr environment; 45 | }; 46 | 47 | template<> 48 | struct value_traits { 49 | typedef FunValue type; 50 | }; 51 | 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /lib/Value/IntegerValue.cpp: -------------------------------------------------------------------------------- 1 | #include "IntegerValue.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace hap { 8 | 9 | bool IntegerValue::equal(const Expression& expression) const { 10 | if (const auto that 11 | = dynamic_cast(&expression)) { 12 | return value == that->value; 13 | } 14 | return false; 15 | } 16 | 17 | shared_ptr IntegerValue::eval 18 | (Context&, const shared_ptr) const { 19 | return make_shared(*this); 20 | } 21 | 22 | bool IntegerValue::less(const Value& that) const { 23 | return Value::less(that) 24 | || (that.type() == Value::INTEGER 25 | && value < static_cast(that).value); 26 | } 27 | 28 | void IntegerValue::write(ostream& stream) const { 29 | stream << value; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lib/Value/IntegerValue.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_INTEGERVALUE_H 2 | #define HAP_INTEGERVALUE_H 3 | 4 | #include "Value.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class IntegerValue : public Value { 11 | public: 12 | IntegerValue(int32_t value) 13 | : value(value) {} 14 | virtual Value::Type type() const final override { 15 | return INTEGER; 16 | } 17 | virtual IntegerValue* copy() const final override { 18 | return new IntegerValue(*this); 19 | } 20 | virtual std::shared_ptr eval 21 | (Context&, std::shared_ptr) const final override; 22 | virtual bool less(const Value&) const final override; 23 | int32_t value; 24 | protected: 25 | virtual bool equal(const Expression&) const final override; 26 | virtual void write(std::ostream&) const final override; 27 | }; 28 | 29 | template<> 30 | struct value_traits { 31 | typedef IntegerValue type; 32 | }; 33 | 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /lib/Value/ListValue.cpp: -------------------------------------------------------------------------------- 1 | #include "ListValue.h" 2 | 3 | #include "UndefinedValue.h" 4 | #include "indirect_compare.h" 5 | 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | namespace hap { 12 | 13 | bool ListValue::equal(const Expression& expression) const { 14 | if (const auto that 15 | = dynamic_cast(&expression)) { 16 | return values.size() == that->values.size() 17 | && std::equal 18 | (begin(values), end(values), 19 | begin(that->values), indirect_equal()); 20 | } 21 | return false; 22 | } 23 | 24 | ListValue::ListValue(const ListValue& that) { 25 | for (const auto& value : that.values) 26 | values.push_back(shared_ptr(value->copy())); 27 | } 28 | 29 | const shared_ptr& ListValue::operator[](const int index) const { 30 | if (index < 0 || index >= values.size()) { 31 | ostringstream message; 32 | message 33 | << "list subscript out of range (" 34 | << index << "/" << values.size() << ")"; 35 | throw runtime_error(message.str()); 36 | } 37 | return values[index]; 38 | } 39 | 40 | shared_ptr& ListValue::operator[](const int index) { 41 | if (index < 0 || index >= values.size()) { 42 | ostringstream message; 43 | message 44 | << "list subscript out of range (" 45 | << index << "/" << values.size() << ")"; 46 | throw runtime_error(message.str()); 47 | } 48 | return values[index]; 49 | } 50 | 51 | bool ListValue::less(const Value& that) const { 52 | return Value::less(that) 53 | || (that.type() == Value::LIST 54 | && values < static_cast(that).values); 55 | } 56 | 57 | void ListValue::write(ostream& stream) const { 58 | stream << "[ "; 59 | for (const auto& value : values) 60 | stream << *value << ", "; 61 | stream << ']'; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /lib/Value/ListValue.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_LISTVALUE_H 2 | #define HAP_LISTVALUE_H 3 | 4 | #include "Value.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class ListValue : public Value { 11 | public: 12 | ListValue() {} 13 | void push(std::shared_ptr value) { 14 | values.push_back(value); 15 | } 16 | const std::shared_ptr& operator[](int) const; 17 | std::shared_ptr& operator[](int); 18 | virtual Value::Type type() const final override { 19 | return LIST; 20 | } 21 | virtual ListValue* copy() const final override { 22 | return new ListValue(*this); 23 | } 24 | virtual bool less(const Value&) const final override; 25 | std::vector> values; 26 | protected: 27 | virtual bool equal(const Expression&) const final override; 28 | virtual void write(std::ostream&) const final override; 29 | private: 30 | ListValue(const ListValue&); 31 | }; 32 | 33 | template<> 34 | struct value_traits { 35 | typedef ListValue type; 36 | }; 37 | 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /lib/Value/MapValue.cpp: -------------------------------------------------------------------------------- 1 | #include "MapValue.h" 2 | 3 | #include "UndefinedValue.h" 4 | #include "indirect_compare.h" 5 | 6 | #include 7 | 8 | using namespace std; 9 | 10 | namespace hap { 11 | 12 | std::shared_ptr& MapValue::operator[] 13 | (const std::shared_ptr key) { 14 | const auto existing = find_if(begin(values), end(values), 15 | [key](const pair, shared_ptr>& entry) { 16 | return entry.first->equal(*key); 17 | }); 18 | if (existing == end(values)) { 19 | values[key].reset(new UndefinedValue()); 20 | return values[key]; 21 | } 22 | return existing->second; 23 | } 24 | 25 | bool MapValue::equal(const Expression& expression) const { 26 | if (const auto that 27 | = dynamic_cast(&expression)) { 28 | return values.size() == that->values.size() 29 | && std::equal 30 | (begin(values), end(values), 31 | begin(that->values), pair_indirect_equal()); 32 | } 33 | return false; 34 | } 35 | 36 | MapValue::MapValue(const MapValue& that) { 37 | for (const auto& value : that.values) 38 | values.insert(make_pair 39 | (shared_ptr(value.first->copy()), 40 | shared_ptr(value.second->copy()))); 41 | } 42 | 43 | bool MapValue::less(const Value& that) const { 44 | return Value::less(that) 45 | || (that.type() == Value::MAP 46 | && values < static_cast(that).values); 47 | } 48 | 49 | void MapValue::write(ostream& stream) const { 50 | stream << "{ "; 51 | for (const auto& value : values) 52 | stream << *value.first << ": " << *value.second << ", "; 53 | stream << '}'; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /lib/Value/MapValue.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_MAPVALUE_H 2 | #define HAP_MAPVALUE_H 3 | 4 | #include "Value.h" 5 | #include "indirect_compare.h" 6 | 7 | #include 8 | 9 | namespace hap { 10 | 11 | class MapValue : public Value { 12 | public: 13 | MapValue() {} 14 | const std::shared_ptr& operator[](std::shared_ptr) const; 15 | std::shared_ptr& operator[](std::shared_ptr); 16 | void insert 17 | (const std::shared_ptr key, 18 | const std::shared_ptr value) { 19 | values.insert(std::make_pair(key, value)); 20 | } 21 | virtual Value::Type type() const final override { 22 | return MAP; 23 | } 24 | virtual MapValue* copy() const final override { 25 | return new MapValue(*this); 26 | } 27 | virtual bool less(const Value&) const final override; 28 | protected: 29 | virtual bool equal(const Expression&) const final override; 30 | virtual void write(std::ostream&) const final override; 31 | private: 32 | MapValue(const MapValue&); 33 | std::map 34 | , 35 | std::shared_ptr, 36 | indirect_compare> values; 37 | }; 38 | 39 | template<> 40 | struct value_traits { 41 | typedef MapValue type; 42 | }; 43 | 44 | } 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /lib/Value/StringValue.cpp: -------------------------------------------------------------------------------- 1 | #include "StringValue.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace hap { 8 | 9 | bool StringValue::equal(const Expression& expression) const { 10 | if (const auto that 11 | = dynamic_cast(&expression)) { 12 | return value == that->value; 13 | } 14 | return false; 15 | } 16 | 17 | shared_ptr StringValue::eval 18 | (Context&, const shared_ptr) const { 19 | return make_shared(*this); 20 | } 21 | 22 | bool StringValue::less(const Value& that) const { 23 | return Value::less(that) 24 | || (that.type() == Value::STRING 25 | && value < static_cast(that).value); 26 | } 27 | 28 | void StringValue::write(ostream& stream) const { 29 | stream << '"' << value << '"'; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lib/Value/StringValue.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_STRINGVALUE_H 2 | #define HAP_STRINGVALUE_H 3 | 4 | #include "Value.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class StringValue : public Value { 11 | public: 12 | StringValue(const std::string& value) 13 | : value(value) {} 14 | virtual Value::Type type() const final override { 15 | return STRING; 16 | } 17 | virtual StringValue* copy() const final override { 18 | return new StringValue(*this); 19 | } 20 | virtual std::shared_ptr eval 21 | (Context&, std::shared_ptr) const final override; 22 | virtual bool less(const Value&) const final override; 23 | std::string value; 24 | protected: 25 | virtual bool equal(const Expression&) const final override; 26 | virtual void write(std::ostream&) const final override; 27 | }; 28 | 29 | template<> 30 | struct value_traits { 31 | typedef StringValue type; 32 | }; 33 | 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /lib/Value/UndefinedValue.cpp: -------------------------------------------------------------------------------- 1 | #include "UndefinedValue.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace hap { 8 | 9 | bool UndefinedValue::equal(const Expression& expression) const { 10 | return dynamic_cast(&expression); 11 | } 12 | 13 | shared_ptr UndefinedValue::eval 14 | (Context&, const shared_ptr) const { 15 | return make_shared(*this); 16 | } 17 | 18 | bool UndefinedValue::less(const Value& that) const { 19 | return Value::less(that); 20 | } 21 | 22 | void UndefinedValue::write(ostream& stream) const { 23 | stream << "undefined"; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /lib/Value/UndefinedValue.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_UNDEFINEDVALUE_H 2 | #define HAP_UNDEFINEDVALUE_H 3 | 4 | #include "Value.h" 5 | 6 | namespace hap { 7 | 8 | class UndefinedValue : public Value { 9 | public: 10 | UndefinedValue() = default; 11 | UndefinedValue(const UndefinedValue&) = default; 12 | virtual Value::Type type() const final override { 13 | return UNDEFINED; 14 | } 15 | virtual UndefinedValue* copy() const final override { 16 | return new UndefinedValue(*this); 17 | } 18 | virtual std::shared_ptr eval 19 | (Context&, std::shared_ptr) const final override; 20 | virtual bool less(const Value&) const final override; 21 | protected: 22 | virtual bool equal(const Expression&) const final override; 23 | virtual void write(std::ostream&) const final override; 24 | }; 25 | 26 | template<> 27 | struct value_traits { 28 | typedef UndefinedValue type; 29 | }; 30 | 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /lib/Value/Value.cpp: -------------------------------------------------------------------------------- 1 | #include "Value.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace hap { 8 | 9 | Value::~Value() {} 10 | 11 | void Value::assert_type(Type expected) const { 12 | auto actual(type()); 13 | if (actual == expected) 14 | return; 15 | ostringstream message; 16 | message << "expected " << expected << " but got " << actual; 17 | throw runtime_error(message.str()); 18 | } 19 | 20 | bool Value::less(const Value& that) const { 21 | return type() < that.type(); 22 | } 23 | 24 | ostream& operator<<(ostream& stream, const Value::Type& type) { 25 | switch (type) { 26 | case Value::UNDEFINED: return stream << "undefined"; 27 | case Value::BOOLEAN: return stream << "boolean"; 28 | case Value::FLOAT: return stream << "float"; 29 | case Value::FUNCTION: return stream << "function"; 30 | case Value::INTEGER: return stream << "integer"; 31 | case Value::LIST: return stream << "list"; 32 | case Value::MAP: return stream << "map"; 33 | case Value::STRING: return stream << "string"; 34 | } 35 | } 36 | 37 | bool operator<(const Value& a, const Value& b) { 38 | return a.less(b); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /lib/Value/Value.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_VALUE_H 2 | #define HAP_VALUE_H 3 | 4 | #include "Atomic.h" 5 | #include "Expression.h" 6 | 7 | namespace hap { 8 | 9 | class Value : public Expression { 10 | public: 11 | enum Type { 12 | UNDEFINED, 13 | BOOLEAN, 14 | FLOAT, 15 | FUNCTION, 16 | INTEGER, 17 | LIST, 18 | MAP, 19 | STRING, 20 | }; 21 | void assert_type(Type) const; 22 | virtual ~Value(); 23 | virtual std::shared_ptr eval 24 | (Context&, std::shared_ptr) const { 25 | return std::shared_ptr(copy()); 26 | } 27 | virtual Value* copy() const = 0; 28 | virtual bool less(const Value&) const = 0; 29 | virtual Type type() const = 0; 30 | }; 31 | 32 | template 33 | struct value_traits {}; 34 | 35 | template 36 | std::shared_ptr::type> eval_as 37 | (const std::shared_ptr expression, 38 | Context& context, 39 | const std::shared_ptr environment) { 40 | const auto value(expression->eval(context, environment)); 41 | value->assert_type(T); 42 | return std::static_pointer_cast::type>(value); 43 | } 44 | 45 | template 46 | std::shared_ptr::type> atomic_eval_as 47 | (const std::shared_ptr expression, 48 | Context& context, 49 | const std::shared_ptr environment) { 50 | std::shared_ptr::type> result; 51 | { 52 | Atomic atomic(context); 53 | result = eval_as(expression, context, environment); 54 | } 55 | return result; 56 | } 57 | 58 | std::ostream& operator<<(std::ostream&, const Value::Type&); 59 | bool operator<(const Value&, const Value&); 60 | 61 | } 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /lib/binary.cpp: -------------------------------------------------------------------------------- 1 | #include "binary.h" 2 | 3 | #include "BooleanValue.h" 4 | #include "DotExpression.h" 5 | #include "Environment.h" 6 | #include "FloatValue.h" 7 | #include "IdentifierExpression.h" 8 | #include "IntegerValue.h" 9 | #include "ListValue.h" 10 | #include "MapValue.h" 11 | #include "StringValue.h" 12 | #include "SubscriptExpression.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace std; 19 | 20 | namespace hap { 21 | 22 | namespace binary { 23 | 24 | #define BINARY_OPERATOR(NAME, ASSOCIATIVITY, PRECEDENCE, IMPLEMENTATION) \ 25 | make_pair(NAME, Operator \ 26 | (Operator::BINARY, \ 27 | NAME, \ 28 | Operator::ASSOCIATIVITY, \ 29 | Operator::PRECEDENCE, \ 30 | binary::IMPLEMENTATION)) 31 | 32 | map operators { 33 | BINARY_OPERATOR("*", LEFT, MULTIPLICATIVE, multiply), 34 | BINARY_OPERATOR("/", LEFT, MULTIPLICATIVE, divide), 35 | BINARY_OPERATOR("mod", LEFT, MULTIPLICATIVE, modulate), 36 | BINARY_OPERATOR("+", LEFT, ADDITIVE, add), 37 | BINARY_OPERATOR("-", LEFT, ADDITIVE, subtract), 38 | BINARY_OPERATOR("<<", LEFT, SHIFTING, shift_left), 39 | BINARY_OPERATOR(">>", LEFT, SHIFTING, shift_right), 40 | BINARY_OPERATOR("<", LEFT, RELATIONAL, lt), 41 | BINARY_OPERATOR(">=", LEFT, RELATIONAL, ge), 42 | BINARY_OPERATOR(">", LEFT, RELATIONAL, gt), 43 | BINARY_OPERATOR("<=", LEFT, RELATIONAL, le), 44 | BINARY_OPERATOR("==", LEFT, RELATIONAL, eq), 45 | BINARY_OPERATOR("<>", LEFT, RELATIONAL, ne), 46 | BINARY_OPERATOR("and", LEFT, AND, and_), 47 | BINARY_OPERATOR("xor", LEFT, XOR, xor_), 48 | BINARY_OPERATOR("or", LEFT, OR, or_), 49 | BINARY_OPERATOR("=", RIGHT, ASSIGNMENT, assign), 50 | }; 51 | 52 | #undef BINARY_OPERATOR 53 | 54 | template 55 | shared_ptr integer_arithmetic 56 | (Function function, 57 | Context& context, 58 | const shared_ptr environment, 59 | const shared_ptr& left, 60 | const shared_ptr& right) { 61 | return make_shared(function 62 | (eval_as(left, context, environment)->value, 63 | eval_as(right, context, environment)->value)); 64 | } 65 | 66 | template 67 | 71 | shared_ptr arithmetic 72 | (FloatFunction float_function, 73 | IntegerFunction integer_function, 74 | Context& context, 75 | const shared_ptr environment, 76 | const shared_ptr& left, 77 | const shared_ptr& right) { 78 | 79 | const auto left_value(left->eval(context, environment)); 80 | const auto right_value(right->eval(context, environment)); 81 | const auto left_type(left_value->type()); 82 | const auto right_type(right_value->type()); 83 | 84 | const auto fail = [](Value::Type type) { 85 | ostringstream message; 86 | message << "expected integer or float but got " << type; 87 | throw runtime_error(message.str()); 88 | }; 89 | 90 | switch (left_type) { 91 | case Value::INTEGER: 92 | switch (right_type) { 93 | case Value::INTEGER: 94 | return make_shared(integer_function 95 | (static_cast(left_value.get())->value, 96 | static_cast(right_value.get())->value)); 97 | case Value::FLOAT: 98 | return make_shared(float_function 99 | (double(static_cast(left_value.get())->value), 100 | static_cast(right_value.get())->value)); 101 | default: 102 | fail(right_type); 103 | } 104 | 105 | case Value::FLOAT: 106 | switch (right_type) { 107 | case Value::INTEGER: 108 | return make_shared(float_function 109 | (static_cast(left_value.get())->value, 110 | double(static_cast(right_value.get())->value))); 111 | case Value::FLOAT: 112 | return make_shared(float_function 113 | (static_cast(left_value.get())->value, 114 | static_cast(right_value.get())->value)); 115 | default: 116 | fail(right_type); 117 | } 118 | 119 | default: 120 | fail(left_type); 121 | } 122 | throw runtime_error("impossible"); 123 | } 124 | 125 | shared_ptr multiply 126 | (Context& context, 127 | const shared_ptr environment, 128 | const shared_ptr& left, 129 | const shared_ptr& right) { 130 | return arithmetic 131 | (multiplies(), multiplies(), 132 | context, environment, left, right); 133 | } 134 | 135 | shared_ptr divide 136 | (Context& context, 137 | const shared_ptr environment, 138 | const shared_ptr& left, 139 | const shared_ptr& right) { 140 | return arithmetic 141 | (divides(), divides(), 142 | context, environment, left, right); 143 | } 144 | 145 | shared_ptr modulate 146 | (Context& context, 147 | const shared_ptr environment, 148 | const shared_ptr& left, 149 | const shared_ptr& right) { 150 | return arithmetic 151 | (static_cast(fmod), 152 | modulus(), 153 | context, environment, left, right); 154 | } 155 | 156 | shared_ptr add 157 | (Context& context, 158 | const shared_ptr environment, 159 | const shared_ptr& left, 160 | const shared_ptr& right) { 161 | return arithmetic 162 | (plus(), plus(), 163 | context, environment, left, right); 164 | } 165 | 166 | shared_ptr subtract 167 | (Context& context, 168 | const shared_ptr environment, 169 | const shared_ptr& left, 170 | const shared_ptr& right) { 171 | return arithmetic 172 | (minus(), minus(), 173 | context, environment, left, right); 174 | } 175 | 176 | template 177 | struct shifts_left { 178 | T operator()(T a, T b) { 179 | return a << b; 180 | } 181 | }; 182 | 183 | shared_ptr shift_left 184 | (Context& context, 185 | const shared_ptr environment, 186 | const shared_ptr& left, 187 | const shared_ptr& right) { 188 | return integer_arithmetic 189 | (shifts_left(), context, environment, left, right); 190 | } 191 | 192 | template 193 | struct shifts_right { 194 | T operator()(T a, T b) { 195 | return a >> b; 196 | } 197 | }; 198 | 199 | shared_ptr shift_right 200 | (Context& context, 201 | const shared_ptr environment, 202 | const shared_ptr& left, 203 | const shared_ptr& right) { 204 | return integer_arithmetic 205 | (shifts_right(), context, environment, left, right); 206 | } 207 | 208 | template 209 | 212 | shared_ptr relational 213 | (FloatFunction float_function, 214 | IntegerFunction integer_function, 215 | Args&&... args) { 216 | return arithmetic 217 | 218 | (float_function, integer_function, forward(args)...); 219 | } 220 | 221 | shared_ptr lt 222 | (Context& context, 223 | const shared_ptr environment, 224 | const shared_ptr& left, 225 | const shared_ptr& right) { 226 | return relational 227 | (less(), less(), 228 | context, environment, left, right); 229 | } 230 | 231 | shared_ptr ge 232 | (Context& context, 233 | const shared_ptr environment, 234 | const shared_ptr& left, 235 | const shared_ptr& right) { 236 | return relational 237 | (greater_equal(), greater_equal(), 238 | context, environment, left, right); 239 | } 240 | 241 | shared_ptr gt 242 | (Context& context, 243 | const shared_ptr environment, 244 | const shared_ptr& left, 245 | const shared_ptr& right) { 246 | return relational 247 | (greater(), greater(), 248 | context, environment, left, right); 249 | } 250 | 251 | shared_ptr le 252 | (Context& context, 253 | const shared_ptr environment, 254 | const shared_ptr& left, 255 | const shared_ptr& right) { 256 | return relational 257 | (less_equal(), less_equal(), 258 | context, environment, left, right); 259 | } 260 | 261 | shared_ptr eq 262 | (Context& context, 263 | const shared_ptr environment, 264 | const shared_ptr& left, 265 | const shared_ptr& right) { 266 | return relational 267 | (equal_to(), equal_to(), 268 | context, environment, left, right); 269 | } 270 | 271 | shared_ptr ne 272 | (Context& context, 273 | const shared_ptr environment, 274 | const shared_ptr& left, 275 | const shared_ptr& right) { 276 | return relational 277 | (not_equal_to(), not_equal_to(), 278 | context, environment, left, right); 279 | } 280 | 281 | shared_ptr and_ 282 | (Context& context, 283 | const shared_ptr environment, 284 | const shared_ptr& left, 285 | const shared_ptr& right) { 286 | return make_shared 287 | (eval_as(left, context, environment)->value 288 | && eval_as(right, context, environment)->value); 289 | } 290 | 291 | shared_ptr xor_ 292 | (Context& context, 293 | const shared_ptr environment, 294 | const shared_ptr& left, 295 | const shared_ptr& right) { 296 | return make_shared 297 | (eval_as(left, context, environment)->value 298 | != eval_as(right, context, environment)->value); 299 | } 300 | 301 | shared_ptr or_ 302 | (Context& context, 303 | const shared_ptr environment, 304 | const shared_ptr& left, 305 | const shared_ptr& right) { 306 | return make_shared 307 | (eval_as(left, context, environment)->value 308 | || eval_as(right, context, environment)->value); 309 | } 310 | 311 | shared_ptr assign 312 | (Context& context, 313 | const shared_ptr environment, 314 | const shared_ptr& left, 315 | const shared_ptr& right) { 316 | if (const auto left_identifier 317 | = dynamic_cast(left.get())) { 318 | const auto value(right->eval(context, environment)); 319 | (*environment)[left_identifier->identifier] = value; 320 | return value; 321 | } else if (const auto dot 322 | = dynamic_cast(left.get())) { 323 | const auto map(eval_as(dot->expression, context, environment)); 324 | const auto value(right->eval(context, environment)); 325 | (*map)[make_shared(dot->key)] = value; 326 | return value; 327 | } else if (const auto subscript 328 | = dynamic_cast(left.get())) { 329 | const auto container(subscript->expression->eval(context, environment)); 330 | const auto key(subscript->subscript->eval(context, environment)); 331 | const auto value(right->eval(context, environment)); 332 | if (const auto list 333 | = dynamic_cast(container.get())) { 334 | key->assert_type(Value::INTEGER); 335 | (*list)[static_cast(key.get())->value] = value; 336 | } else if (const auto map 337 | = dynamic_cast(container.get())) { 338 | (*map)[key] = value; 339 | } 340 | return value; 341 | } 342 | throw runtime_error("non-lvalue in assignment"); 343 | } 344 | 345 | } 346 | 347 | } 348 | -------------------------------------------------------------------------------- /lib/binary.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_BINARY_H 2 | #define HAP_BINARY_H 3 | 4 | #include "Operator.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace hap { 10 | 11 | namespace binary { 12 | 13 | extern std::map operators; 14 | 15 | Operator::Binary 16 | multiply, 17 | divide, 18 | modulate, 19 | add, 20 | subtract, 21 | shift_left, 22 | shift_right, 23 | lt, 24 | ge, 25 | gt, 26 | le, 27 | eq, 28 | ne, 29 | and_, 30 | xor_, 31 | or_, 32 | assign, 33 | comma; 34 | 35 | } 36 | 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /lib/flow.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_FLOW_H 2 | #define HAP_FLOW_H 3 | 4 | #include "Value.h" 5 | 6 | #include 7 | 8 | namespace hap { 9 | 10 | class Value; 11 | 12 | namespace flow { 13 | 14 | struct Exit {}; 15 | 16 | struct Last {}; 17 | 18 | struct Next {}; 19 | 20 | struct Redo {}; 21 | 22 | struct Return { 23 | Return(std::shared_ptr value) 24 | : value(value) {} 25 | std::shared_ptr value; 26 | }; 27 | 28 | } 29 | 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /lib/indirect_compare.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_INDIRECT_COMPARE_H 2 | #define HAP_INDIRECT_COMPARE_H 3 | 4 | #include 5 | #include 6 | 7 | template> 8 | class indirect_compare : public std::binary_function { 9 | public: 10 | bool operator()(const T* const a, const T* const b) const { 11 | return compare(*a, *b); 12 | } 13 | bool operator() 14 | (const std::shared_ptr& a, 15 | const std::shared_ptr& b) const { 16 | return compare(*a, *b); 17 | } 18 | bool operator() 19 | (const std::shared_ptr& a, 20 | const std::shared_ptr& b) const { 21 | return compare(*a, *b); 22 | } 23 | private: 24 | Compare compare; 25 | }; 26 | 27 | template> 28 | class pair_indirect_compare 29 | : public std::binary_function { 30 | private: 31 | typedef std::shared_ptr shared_T; 32 | typedef std::shared_ptr shared_U; 33 | public: 34 | bool operator() 35 | (const std::pair& a, 36 | const std::pair& b) const { 37 | return compare(*a.first, *b.first) && compare(*a.second, *b.second); 38 | } 39 | bool operator() 40 | (const std::pair& a, 41 | const std::pair& b) const { 42 | return compare(*a.first, *b.first) && compare(*a.second, *b.second); 43 | } 44 | private: 45 | Compare compare; 46 | }; 47 | 48 | template 49 | using indirect_equal = indirect_compare>; 50 | 51 | template 52 | using pair_indirect_equal = pair_indirect_compare>; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /lib/tokenize.cpp: -------------------------------------------------------------------------------- 1 | #include "tokenize.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | namespace hap { 12 | 13 | enum State { 14 | NORMAL, 15 | COMMENT, 16 | FLOAT, 17 | IDENTIFIER, 18 | INTEGER, 19 | OPERATOR, 20 | STRING, 21 | STRING_ESCAPE, 22 | }; 23 | 24 | const map single_character_tokens { 25 | make_pair('(', Token::LEFT_PARENTHESIS), 26 | make_pair(')', Token::RIGHT_PARENTHESIS), 27 | make_pair(',', Token::COMMA), 28 | make_pair('.', Token::DOT), 29 | make_pair(':', Token::COLON), 30 | make_pair(';', Token::SEMICOLON), 31 | make_pair('[', Token::LEFT_BRACKET), 32 | make_pair(']', Token::RIGHT_BRACKET), 33 | make_pair('{', Token::LEFT_BRACE), 34 | make_pair('}', Token::RIGHT_BRACE), 35 | }; 36 | 37 | vector tokenize(istream& stream) { 38 | vector tokens; 39 | string token; 40 | int character = 0; 41 | const string operator_characters = "!$%&*+-./<=>?@\\^|~"; 42 | State state = NORMAL; 43 | bool need = true; 44 | while (character != EOF) { 45 | if (need) 46 | character = stream.get(); 47 | need = true; 48 | switch (state) { 49 | case NORMAL: 50 | switch (character) { 51 | case EOF: 52 | break; 53 | case '#': 54 | state = COMMENT; 55 | break; 56 | case '"': 57 | token = string(1, character); 58 | state = STRING; 59 | break; 60 | default: 61 | if (isspace(character)) 62 | break; 63 | if (character == '_' || isalpha(character)) { 64 | token = string(1, character); 65 | state = IDENTIFIER; 66 | break; 67 | } 68 | if (isdigit(character)) { 69 | token = string(1, character); 70 | state = INTEGER; 71 | break; 72 | } 73 | { 74 | const map::const_iterator found 75 | = single_character_tokens.find(character); 76 | if (found != single_character_tokens.end()) { 77 | tokens.push_back 78 | (Token(found->second, string(1, character))); 79 | break; 80 | } 81 | } 82 | if (operator_characters.find(character) != string::npos) { 83 | token = string(1, character); 84 | state = OPERATOR; 85 | break; 86 | } 87 | { 88 | ostringstream message; 89 | message 90 | << "Invalid character '" 91 | << character 92 | << "'."; 93 | throw runtime_error(message.str()); 94 | } 95 | } 96 | break; 97 | case COMMENT: 98 | if (character == '\n' || character == EOF) 99 | state = NORMAL; 100 | break; 101 | case FLOAT: 102 | if (isdigit(character)) { 103 | token += character; 104 | } else { 105 | tokens.push_back(Token(Token::FLOAT, token)); 106 | state = NORMAL; 107 | need = false; 108 | } 109 | break; 110 | case IDENTIFIER: 111 | if (character == '_' 112 | || isalpha(character) 113 | || isdigit(character)) { 114 | token += character; 115 | } else { 116 | tokens.push_back(Token(Token::IDENTIFIER, token)); 117 | state = NORMAL; 118 | need = false; 119 | } 120 | break; 121 | case INTEGER: 122 | if (isdigit(character)) { 123 | token += character; 124 | } else if (character == '.') { 125 | token += character; 126 | state = FLOAT; 127 | } else { 128 | tokens.push_back(Token(Token::INTEGER, token)); 129 | state = NORMAL; 130 | need = false; 131 | } 132 | break; 133 | case OPERATOR: 134 | if (operator_characters.find(character) != string::npos) { 135 | token += character; 136 | } else { 137 | tokens.push_back(Token(Token::OPERATOR, token)); 138 | state = NORMAL; 139 | need = false; 140 | } 141 | break; 142 | case STRING: 143 | if (character == EOF) 144 | throw runtime_error("EOF in string."); 145 | switch (character) { 146 | case '"': 147 | token += character; 148 | tokens.push_back(Token(Token::STRING, token)); 149 | state = NORMAL; 150 | break; 151 | case '\\': 152 | state = STRING_ESCAPE; 153 | break; 154 | default: 155 | token += character; 156 | } 157 | break; 158 | case STRING_ESCAPE: 159 | if (character == EOF) 160 | throw runtime_error("EOF in string escape."); 161 | switch (character) { 162 | case '\\': 163 | token += '\\'; 164 | break; 165 | case '"': 166 | token += '"'; 167 | break; 168 | case 'a': 169 | token += '\a'; 170 | break; 171 | case 'b': 172 | token += '\b'; 173 | break; 174 | case 'f': 175 | token += '\f'; 176 | break; 177 | case 'n': 178 | token += '\n'; 179 | break; 180 | case 'r': 181 | token += '\r'; 182 | break; 183 | case 't': 184 | token += '\t'; 185 | break; 186 | case 'v': 187 | token += '\v'; 188 | break; 189 | default: 190 | { 191 | ostringstream message; 192 | message << "invalid escape '\\" << character << "'"; 193 | throw runtime_error(message.str()); 194 | } 195 | } 196 | state = STRING; 197 | break; 198 | } 199 | } 200 | return tokens; 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /lib/tokenize.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_TOKENIZE_H 2 | #define HAP_TOKENIZE_H 3 | 4 | #include "Token.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace hap { 10 | 11 | std::vector tokenize(std::istream&); 12 | 13 | } 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /lib/unary.cpp: -------------------------------------------------------------------------------- 1 | #include "unary.h" 2 | 3 | #include "BooleanValue.h" 4 | #include "IntegerValue.h" 5 | 6 | #include 7 | 8 | using namespace std; 9 | 10 | namespace hap { 11 | 12 | namespace unary { 13 | 14 | #define UNARY_OPERATOR(NAME, IMPLEMENTATION) \ 15 | make_pair(NAME, Operator \ 16 | (Operator::UNARY, \ 17 | NAME, \ 18 | Operator::RIGHT, \ 19 | Operator::TIGHTEST, \ 20 | unary::IMPLEMENTATION)) \ 21 | 22 | map operators { 23 | UNARY_OPERATOR("+", identity), 24 | UNARY_OPERATOR("-", negate), 25 | UNARY_OPERATOR("not", not_), 26 | }; 27 | 28 | #undef UNARY_OPERATOR 29 | 30 | shared_ptr identity 31 | (Context& context, 32 | const shared_ptr environment, 33 | const shared_ptr& expression) { 34 | auto value(expression->eval(context, environment)); 35 | value->assert_type(Value::INTEGER); 36 | return value; 37 | } 38 | 39 | shared_ptr negate 40 | (Context& context, 41 | const shared_ptr environment, 42 | const shared_ptr& expression) { 43 | auto value(expression->eval(context, environment)); 44 | value->assert_type(Value::INTEGER); 45 | shared_ptr integer 46 | (static_pointer_cast(value)); 47 | integer->value = -integer->value; 48 | return static_pointer_cast(integer); 49 | } 50 | 51 | template 52 | shared_ptr logical 53 | (F function, 54 | Context& context, 55 | const shared_ptr environment, 56 | const shared_ptr& expression) { 57 | auto a(expression->eval(context, environment)); 58 | a->assert_type(Value::BOOLEAN); 59 | auto b(static_pointer_cast(a)); 60 | return make_shared(function(b->value)); 61 | } 62 | 63 | shared_ptr not_ 64 | (Context& context, 65 | const shared_ptr environment, 66 | const shared_ptr& expression) { 67 | return logical(logical_not(), context, environment, expression); 68 | } 69 | 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /lib/unary.h: -------------------------------------------------------------------------------- 1 | #ifndef HAP_UNARY_H 2 | #define HAP_UNARY_H 3 | 4 | #include "Operator.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace hap { 10 | 11 | namespace unary { 12 | 13 | extern std::map operators; 14 | 15 | Operator::Unary 16 | identity, 17 | negate, 18 | not_; 19 | 20 | } 21 | 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/and.hap: -------------------------------------------------------------------------------- 1 | fun returns_false() { 2 | trace "false"; 3 | ret false; 4 | } 5 | 6 | fun returns_true() { 7 | trace "true"; 8 | ret true; 9 | } 10 | 11 | returns_false() and returns_false(); 12 | returns_false() and returns_true(); 13 | returns_true() and returns_false(); 14 | returns_true() and returns_true(); 15 | -------------------------------------------------------------------------------- /test/and.out.expect: -------------------------------------------------------------------------------- 1 | "false" 2 | "false" 3 | "true" 4 | "false" 5 | "true" 6 | "true" 7 | -------------------------------------------------------------------------------- /test/arithmetic.hap: -------------------------------------------------------------------------------- 1 | trace 0 + 0; 2 | trace 0 + 0.0; 3 | trace 0.0 + 0; 4 | trace 0.0 + 0.0; 5 | trace 0 * 0; 6 | trace 0 * 0.0; 7 | trace 0.0 * 0; 8 | trace 0.0 * 0.0; 9 | trace 1 / 2; 10 | trace 1 / 2.0; 11 | trace 1.0 / 2; 12 | trace 1.0 / 2.0; 13 | trace 0 == 0; 14 | trace 0 == 0.0; 15 | trace 0.0 == 0; 16 | trace 0.0 == 0.0; 17 | trace 0 < 1; 18 | trace 0 < 1.0; 19 | trace 0.0 < 1; 20 | trace 0.0 < 1.0; 21 | trace 12 mod 5; 22 | trace 12 mod 5.25; 23 | trace 12.0 mod 5; 24 | trace 12.0 mod 5.25; 25 | -------------------------------------------------------------------------------- /test/arithmetic.out.expect: -------------------------------------------------------------------------------- 1 | 0 2 | 0.000000000 3 | 0.000000000 4 | 0.000000000 5 | 0 6 | 0.000000000 7 | 0.000000000 8 | 0.000000000 9 | 0 10 | 0.5000000000 11 | 0.5000000000 12 | 0.5000000000 13 | true 14 | true 15 | true 16 | true 17 | true 18 | true 19 | true 20 | true 21 | 2 22 | 1.500000000 23 | 2.000000000 24 | 1.500000000 25 | -------------------------------------------------------------------------------- /test/assign.hap: -------------------------------------------------------------------------------- 1 | var x; 2 | trace x; 3 | x = 0; 4 | trace x; 5 | x = ""; 6 | trace x; 7 | x = false; 8 | trace x; 9 | x = undefined; 10 | trace x; 11 | 12 | var xs = [1, 2, 3]; 13 | xs[0] = "one"; 14 | xs[1] = "two"; 15 | xs[2] = "three"; 16 | trace xs[0]; 17 | trace xs[1]; 18 | trace xs[2]; 19 | 20 | var ps = {one: 1, two: 2, three: 3}; 21 | ps["one"] = "one"; 22 | ps["two"] = "two"; 23 | ps["three"] = "three"; 24 | trace ps.one; 25 | trace ps.two; 26 | trace ps.three; 27 | ps.one = 1; 28 | ps.two = 2; 29 | ps.three = 3; 30 | var one = "one"; 31 | var two = "two"; 32 | var three = "three"; 33 | trace ps[one]; 34 | trace ps[two]; 35 | trace ps[three]; 36 | -------------------------------------------------------------------------------- /test/assign.out.expect: -------------------------------------------------------------------------------- 1 | undefined 2 | 0 3 | "" 4 | false 5 | undefined 6 | "one" 7 | "two" 8 | "three" 9 | "one" 10 | "two" 11 | "three" 12 | 1 13 | 2 14 | 3 15 | -------------------------------------------------------------------------------- /test/atomic.hap: -------------------------------------------------------------------------------- 1 | var x = false; 2 | when (x) { 3 | trace "x"; 4 | } 5 | { 6 | x = true; 7 | x = false; 8 | } 9 | var y = false; 10 | when (y) { 11 | trace "y"; 12 | } 13 | atomic { 14 | y = true; 15 | y = false; 16 | } 17 | -------------------------------------------------------------------------------- /test/atomic.out.expect: -------------------------------------------------------------------------------- 1 | "x" 2 | -------------------------------------------------------------------------------- /test/boolean.hap: -------------------------------------------------------------------------------- 1 | # Literals 2 | trace true; 3 | trace false; 4 | 5 | # AND 6 | trace false and false; 7 | trace false and true; 8 | trace true and false; 9 | trace true and true; 10 | 11 | # OR 12 | trace false or false; 13 | trace false or true; 14 | trace true or false; 15 | trace true or true; 16 | 17 | # XOR 18 | trace false xor false; 19 | trace false xor true; 20 | trace true xor false; 21 | trace true xor true; 22 | -------------------------------------------------------------------------------- /test/boolean.out.expect: -------------------------------------------------------------------------------- 1 | true 2 | false 3 | false 4 | false 5 | false 6 | true 7 | false 8 | true 9 | true 10 | true 11 | false 12 | true 13 | true 14 | false 15 | -------------------------------------------------------------------------------- /test/comment.hap: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # This is a comment containing a hash character (#). 3 | 4 | # This is a comment that ends with EOF. -------------------------------------------------------------------------------- /test/comment.out.expect: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evincarofautumn/Hap/9ac3bdf53c22f82ea573e0359c2f3f6d8d8ac556/test/comment.out.expect -------------------------------------------------------------------------------- /test/curry.hap: -------------------------------------------------------------------------------- 1 | fun multiply(x) 2 | ret lam (y): x * y; 3 | 4 | trace multiply(5)(10); 5 | 6 | fun make_counter(x) { 7 | ret lam() { 8 | x = x + 1; 9 | ret x; 10 | }; 11 | } 12 | 13 | var counter = make_counter(0); 14 | trace counter(); 15 | trace counter(); 16 | trace counter(); 17 | 18 | fun make_cycler(start, end) { 19 | var current = start; 20 | whenever (current > end) 21 | current = start; 22 | ret lam() { 23 | var previous = current; 24 | current = current + 1; 25 | ret previous; 26 | }; 27 | } 28 | 29 | var cycler = make_cycler(0, 2); 30 | for (var i = 0; i < 6; i = i + 1) 31 | trace cycler(); 32 | -------------------------------------------------------------------------------- /test/curry.out.expect: -------------------------------------------------------------------------------- 1 | 50 2 | 1 3 | 2 4 | 3 5 | 0 6 | 1 7 | 2 8 | 0 9 | 1 10 | 2 11 | -------------------------------------------------------------------------------- /test/exit.hap: -------------------------------------------------------------------------------- 1 | var health = 100; 2 | 3 | when (health <= 0) { 4 | trace "Goodbye, cruel world!"; 5 | exit; 6 | } 7 | 8 | trace "Hello, sweet world!"; 9 | 10 | while (true) 11 | health = health - 1; 12 | -------------------------------------------------------------------------------- /test/exit.out.expect: -------------------------------------------------------------------------------- 1 | "Hello, sweet world!" 2 | "Goodbye, cruel world!" 3 | -------------------------------------------------------------------------------- /test/for.hap: -------------------------------------------------------------------------------- 1 | var x = "hello"; 2 | for (var x = 0; x < 10; x = x + 1) { 3 | trace x; 4 | } 5 | trace x; 6 | x = "goodbye"; 7 | trace x; 8 | -------------------------------------------------------------------------------- /test/for.out.expect: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | 3 5 | 4 6 | 5 7 | 6 8 | 7 9 | 8 10 | 9 11 | "hello" 12 | "goodbye" 13 | -------------------------------------------------------------------------------- /test/function.hap: -------------------------------------------------------------------------------- 1 | fun add_long(x, y) { 2 | var sum = x + y; 3 | ret sum; 4 | } 5 | 6 | trace add_long(2, 3); 7 | 8 | fun add_normal(x, y) { 9 | ret x + y; 10 | } 11 | 12 | trace add_normal(3, 4); 13 | 14 | fun add_concise(x, y) 15 | ret x + y; 16 | 17 | trace add_concise(5, 6); 18 | 19 | fun returns_function() { 20 | ret add_concise; 21 | } 22 | 23 | trace (returns_function())(6, 7); 24 | -------------------------------------------------------------------------------- /test/function.out.expect: -------------------------------------------------------------------------------- 1 | 5 2 | 7 3 | 11 4 | 13 5 | -------------------------------------------------------------------------------- /test/if.hap: -------------------------------------------------------------------------------- 1 | if (true) 2 | trace true; 3 | 4 | if (false) { 5 | trace false; 6 | } 7 | 8 | var t = true; 9 | 10 | if (t) { 11 | trace true; 12 | } 13 | 14 | var f = false; 15 | 16 | if (f) 17 | trace false; 18 | -------------------------------------------------------------------------------- /test/if.out.expect: -------------------------------------------------------------------------------- 1 | true 2 | true 3 | -------------------------------------------------------------------------------- /test/lambda.hap: -------------------------------------------------------------------------------- 1 | var anonymous_add_statement = lam(x, y) { 2 | ret x + y; 3 | }; 4 | trace anonymous_add_statement(0, 1); 5 | 6 | var anonymous_add_expression = lam(x, y): x + y; 7 | trace anonymous_add_expression(1, 2); 8 | 9 | var named_add_statement = lam add_statement(x, y) { 10 | ret x + y; 11 | }; 12 | trace named_add_statement(2, 3); 13 | 14 | var named_add_expression = lam add_expression(x, y): x + y; 15 | trace named_add_expression(3, 4); 16 | -------------------------------------------------------------------------------- /test/lambda.out.expect: -------------------------------------------------------------------------------- 1 | 1 2 | 3 3 | 5 4 | 7 5 | -------------------------------------------------------------------------------- /test/list-literal.hap: -------------------------------------------------------------------------------- 1 | # Zero elements. 2 | trace []; 3 | 4 | # One element. 5 | trace [0]; 6 | 7 | # One element with trailing comma. 8 | trace [0,]; 9 | 10 | # Two elements. 11 | trace [0, 1]; 12 | 13 | # Two elements with trailing comma. 14 | trace [0, 1,]; 15 | 16 | # Many elements. 17 | trace [0, 1, 2, 3, 4, 5, 6]; 18 | 19 | # Many elements with trailing comma. 20 | trace [0, 1, 2, 3, 4, 5, 6,]; 21 | -------------------------------------------------------------------------------- /test/list-literal.out.expect: -------------------------------------------------------------------------------- 1 | [ ] 2 | [ 0, ] 3 | [ 0, ] 4 | [ 0, 1, ] 5 | [ 0, 1, ] 6 | [ 0, 1, 2, 3, 4, 5, 6, ] 7 | [ 0, 1, 2, 3, 4, 5, 6, ] 8 | -------------------------------------------------------------------------------- /test/list-reference.hap: -------------------------------------------------------------------------------- 1 | fun make_trigger() { 2 | var reference = [false]; 3 | when (reference[0]) 4 | trace "trigger"; 5 | ret reference; 6 | } 7 | 8 | var reference = make_trigger(); 9 | reference[0] = true; 10 | -------------------------------------------------------------------------------- /test/list-reference.out.expect: -------------------------------------------------------------------------------- 1 | "trigger" 2 | -------------------------------------------------------------------------------- /test/map-literal.hap: -------------------------------------------------------------------------------- 1 | # Zero pairs. 2 | trace {}; 3 | 4 | # One pair. 5 | trace {0:0}; 6 | 7 | # One pair with trailing comma. 8 | trace {0:0,}; 9 | 10 | # Two pairs. 11 | trace {0:0, 1:1}; 12 | 13 | # Two pairs with trailing comma. 14 | trace {0:0, 1:1,}; 15 | 16 | # Many pairs. 17 | trace {0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6}; 18 | 19 | # Many pairs with trailing comma. 20 | trace {0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6,}; 21 | 22 | # One bareword pair. 23 | trace {a:0}; 24 | 25 | # One bareword pair with trailing comma. 26 | trace {a:0,}; 27 | 28 | # Two bareword pairs. 29 | trace {a:0, b:1}; 30 | 31 | # Two bareword pairs with trailing comma. 32 | trace {a:0, b:1,}; 33 | 34 | # Many bareword pairs. 35 | trace {a:0, b:1, c:2, d:3, e:4, f:5, g:6}; 36 | 37 | # Many bareword pairs with trailing comma. 38 | trace {a:0, b:1, c:2, d:3, e:4, f:5, g:6,}; 39 | 40 | var a = 0; 41 | trace { (a): "A" }; 42 | 43 | var a = 0; 44 | trace { (a + 1): "A" }; 45 | -------------------------------------------------------------------------------- /test/map-literal.out.expect: -------------------------------------------------------------------------------- 1 | { } 2 | { 0: 0, } 3 | { 0: 0, } 4 | { 0: 0, 1: 1, } 5 | { 0: 0, 1: 1, } 6 | { 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, } 7 | { 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, } 8 | { "a": 0, } 9 | { "a": 0, } 10 | { "a": 0, "b": 1, } 11 | { "a": 0, "b": 1, } 12 | { "a": 0, "b": 1, "c": 2, "d": 3, "e": 4, "f": 5, "g": 6, } 13 | { "a": 0, "b": 1, "c": 2, "d": 3, "e": 4, "f": 5, "g": 6, } 14 | { 0: "A", } 15 | { 1: "A", } 16 | -------------------------------------------------------------------------------- /test/map-reference.hap: -------------------------------------------------------------------------------- 1 | fun make_trigger() { 2 | var reference = {triggered: false}; 3 | when (reference.triggered) 4 | trace "trigger"; 5 | ret reference; 6 | } 7 | 8 | var reference = make_trigger(); 9 | reference.triggered = true; 10 | -------------------------------------------------------------------------------- /test/map-reference.out.expect: -------------------------------------------------------------------------------- 1 | "trigger" 2 | -------------------------------------------------------------------------------- /test/map.hap: -------------------------------------------------------------------------------- 1 | var map = { a: "a", "b": "b", 0: "zero" }; 2 | trace map.a; 3 | trace map["a"]; 4 | trace map.b; 5 | trace map["b"]; 6 | trace map[0]; 7 | var zero = 0; 8 | trace map[zero]; 9 | -------------------------------------------------------------------------------- /test/map.out.expect: -------------------------------------------------------------------------------- 1 | "a" 2 | "a" 3 | "b" 4 | "b" 5 | "zero" 6 | "zero" 7 | -------------------------------------------------------------------------------- /test/next.hap: -------------------------------------------------------------------------------- 1 | for (var i = 0; i < 10; i = i + 1) { 2 | if (i mod 2 == 0) 3 | next; 4 | trace i; 5 | } 6 | 7 | var i = 0; 8 | while ((i = i + 1) < 10) { 9 | if (i mod 2 == 0) 10 | next; 11 | trace i; 12 | } 13 | -------------------------------------------------------------------------------- /test/next.out.expect: -------------------------------------------------------------------------------- 1 | 1 2 | 3 3 | 5 4 | 7 5 | 9 6 | 1 7 | 3 8 | 5 9 | 7 10 | 9 11 | -------------------------------------------------------------------------------- /test/or.hap: -------------------------------------------------------------------------------- 1 | fun returns_false() { 2 | trace "false"; 3 | ret false; 4 | } 5 | 6 | fun returns_true() { 7 | trace "true"; 8 | ret true; 9 | } 10 | 11 | returns_false() or returns_false(); 12 | returns_false() or returns_true(); 13 | returns_true() or returns_false(); 14 | returns_true() or returns_true(); 15 | -------------------------------------------------------------------------------- /test/or.out.expect: -------------------------------------------------------------------------------- 1 | "false" 2 | "false" 3 | "false" 4 | "true" 5 | "true" 6 | "true" 7 | -------------------------------------------------------------------------------- /test/redo.hap: -------------------------------------------------------------------------------- 1 | var seen_three = false; 2 | for (var i = 1; i < 6; i = i + 1) { 3 | trace i; 4 | if (i == 3 and not seen_three) { 5 | seen_three = true; 6 | redo; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/redo.out.expect: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 3 5 | 4 6 | 5 7 | -------------------------------------------------------------------------------- /test/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | here="$(pwd)" 5 | 6 | if [ "$#" -lt 1 ]; then 7 | echo "Usage: run.sh /path/to/hap [test-name]" >&2 8 | exit 1 9 | fi 10 | 11 | HAP="$1" 12 | shift 13 | 14 | function run_test { 15 | 16 | set +e +E 17 | 18 | test_file="$1" 19 | test_name="$(basename "$test_file" ".hap")" 20 | actual_out="$here/$test_name.out.actual" 21 | expect_out="$here/$test_name.out.expect" 22 | actual_err="$here/$test_name.err.actual" 23 | expect_err="$here/$test_name.err.expect" 24 | 25 | if [ ! -e "$expect_err" ]; then 26 | expect_err=/dev/null 27 | fi 28 | 29 | "$HAP" "$test_file" > "$actual_out" 2> "$actual_err" 30 | 31 | if [ ! -e "$expect_out" ]; then 32 | echo "Test '$test_name' BROKEN." >&2 33 | echo "Expected positive test output ($expect_out) not found." >&2 34 | exit 1 35 | fi 36 | 37 | diff -u "$expect_err" "$actual_err" 38 | if [ "$?" != 0 ]; then 39 | echo "Test '$test_name' FAILED." >&2 40 | echo "Negative test output does not match expected." >&2 41 | echo 42 | echo "Expected:" >&2 43 | cat "$expect_err" >&2 44 | echo 45 | echo "Actual:" >&2 46 | cat "$actual_err" >&2 47 | echo 48 | exit 1 49 | fi 50 | 51 | diff -u "$expect_out" "$actual_out" 52 | if [ "$?" != 0 ]; then 53 | echo "Test '$test_name' FAILED." >&2 54 | echo "Positive test output does not match expected." >&2 55 | exit 1 56 | fi 57 | 58 | set -e -E 59 | 60 | } 61 | 62 | if [ ! -e "$HAP" ]; then 63 | echo "Unable to run tests; missing 'hap' executable." >&2 64 | exit 1 65 | fi 66 | 67 | if [ $# -gt 0 ]; then 68 | for test in "$@"; do 69 | run_test "./$test.hap" 70 | done 71 | else 72 | find . -maxdepth 1 -name '*.hap' | while read test_file; do 73 | run_test "$test_file" 74 | done 75 | fi 76 | -------------------------------------------------------------------------------- /test/subscript.hap: -------------------------------------------------------------------------------- 1 | var list = ["a", "b", "c"]; 2 | trace list; 3 | trace list[0]; 4 | trace list[1]; 5 | trace list[2]; 6 | -------------------------------------------------------------------------------- /test/subscript.out.expect: -------------------------------------------------------------------------------- 1 | [ "a", "b", "c", ] 2 | "a" 3 | "b" 4 | "c" 5 | -------------------------------------------------------------------------------- /test/trace.hap: -------------------------------------------------------------------------------- 1 | trace "Hello, world!" 2 | trace 0 3 | trace [1, 2, 3] 4 | trace [1, 2, 3, ] 5 | trace {a: "a", b: "b"} 6 | -------------------------------------------------------------------------------- /test/trace.out.expect: -------------------------------------------------------------------------------- 1 | "Hello, world!" 2 | 0 3 | [ 1, 2, 3, ] 4 | [ 1, 2, 3, ] 5 | { "a": "a", "b": "b", } 6 | -------------------------------------------------------------------------------- /test/var.hap: -------------------------------------------------------------------------------- 1 | var x; 2 | var y = 0; 3 | trace x; 4 | trace y; 5 | -------------------------------------------------------------------------------- /test/var.out.expect: -------------------------------------------------------------------------------- 1 | undefined 2 | 0 3 | -------------------------------------------------------------------------------- /test/when.hap: -------------------------------------------------------------------------------- 1 | var x = 10; 2 | trace x; 3 | when (x == 20) { 4 | trace x; 5 | } 6 | x = 20; 7 | 8 | var y = 30; 9 | trace y; 10 | fun f() { 11 | ret y == 40; 12 | } 13 | when (f()) { 14 | trace y; 15 | } 16 | y = 40; 17 | -------------------------------------------------------------------------------- /test/when.out.expect: -------------------------------------------------------------------------------- 1 | 10 2 | 20 3 | 30 4 | 40 5 | -------------------------------------------------------------------------------- /test/whenever.hap: -------------------------------------------------------------------------------- 1 | var x = false; 2 | trace x; 3 | whenever (x) { 4 | trace x; 5 | } 6 | x = true; 7 | x = false; 8 | x = true; 9 | x = false; 10 | x = true; 11 | x = true; 12 | -------------------------------------------------------------------------------- /test/whenever.out.expect: -------------------------------------------------------------------------------- 1 | false 2 | true 3 | true 4 | true 5 | -------------------------------------------------------------------------------- /test/while.hap: -------------------------------------------------------------------------------- 1 | var x = 0; 2 | while (x < 10) { 3 | trace x; 4 | x = x + 1; 5 | } 6 | -------------------------------------------------------------------------------- /test/while.out.expect: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | 3 5 | 4 6 | 5 7 | 6 8 | 7 9 | 8 10 | 9 11 | -------------------------------------------------------------------------------- /test/xor.hap: -------------------------------------------------------------------------------- 1 | fun returns_false() { 2 | trace "false"; 3 | ret false; 4 | } 5 | 6 | fun returns_true() { 7 | trace "true"; 8 | ret true; 9 | } 10 | 11 | returns_false() xor returns_false(); 12 | returns_false() xor returns_true(); 13 | returns_true() xor returns_false(); 14 | returns_true() xor returns_true(); 15 | -------------------------------------------------------------------------------- /test/xor.out.expect: -------------------------------------------------------------------------------- 1 | "false" 2 | "false" 3 | "false" 4 | "true" 5 | "true" 6 | "false" 7 | "true" 8 | "true" 9 | -------------------------------------------------------------------------------- /tools/woc.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | my $line = 1; 4 | while (<>) { 5 | print "$ARGV:$line: $_" if length > 80; 6 | ++$line; 7 | $line = 1 if eof; 8 | } 9 | -------------------------------------------------------------------------------- /unit/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | void suite_tokenize(); 7 | void suite_parse(); 8 | 9 | int main(int argc, char** argv) try { 10 | suite_tokenize(); 11 | suite_parse(); 12 | } catch (const exception& error) { 13 | cerr << error.what() << '\n'; 14 | return 1; 15 | } 16 | -------------------------------------------------------------------------------- /unit/parse.cpp: -------------------------------------------------------------------------------- 1 | #include "BinaryExpression.h" 2 | #include "BlockStatement.h" 3 | #include "BooleanValue.h" 4 | #include "CallExpression.h" 5 | #include "ControlStatement.h" 6 | #include "DelStatement.h" 7 | #include "ExpressionStatement.h" 8 | #include "FlowStatement.h" 9 | #include "ForStatement.h" 10 | #include "FunStatement.h" 11 | #include "FunValue.h" 12 | #include "IdentifierExpression.h" 13 | #include "IntegerValue.h" 14 | #include "Parser.h" 15 | #include "RetStatement.h" 16 | #include "SubscriptExpression.h" 17 | #include "UndefinedValue.h" 18 | #include "VarStatement.h" 19 | #include "binary.h" 20 | #include "tokenize.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | using namespace hap; 27 | using namespace std; 28 | 29 | #define TEST_PARSE(NAME, INPUT, EXPECTED) \ 30 | test_parse \ 31 | (__FILE__, __LINE__, \ 32 | NAME, INPUT, unique_ptr(EXPECTED)) 33 | 34 | void test_parse 35 | (const string& file, 36 | const int line, 37 | const string& name, 38 | const string& input, 39 | const unique_ptr expected) { 40 | ostringstream message; 41 | message 42 | << file << ":" << line 43 | << ": Unit test '" << name << "' failed:\n" 44 | << "Expected:\n" << *expected; 45 | shared_ptr actual; 46 | { 47 | const auto environment = make_shared(); 48 | istringstream stream(input); 49 | const auto tokens(tokenize(stream)); 50 | Parser parser(tokens, environment); 51 | try { 52 | actual = parser.accept_program(); 53 | if (actual) 54 | message << "Actual:\n" << *actual << '\n'; 55 | } catch (const exception& failure) { 56 | message << "Failure:\n" << failure.what() << '\n'; 57 | } 58 | } 59 | if (actual && *actual == *expected) 60 | return; 61 | throw runtime_error(message.str()); 62 | } 63 | 64 | void suite_parse() { 65 | { 66 | const auto expected(new BlockStatement()); 67 | TEST_PARSE 68 | ("empty program", 69 | "", 70 | expected); 71 | } 72 | 73 | { 74 | const auto expected 75 | (new BlockStatement{new BlockStatement()}); 76 | TEST_PARSE 77 | ("empty statement", 78 | ";", 79 | expected); 80 | } 81 | 82 | { 83 | const auto expected 84 | (new BlockStatement 85 | {new ExpressionStatement 86 | (new BooleanValue(true))}); 87 | TEST_PARSE 88 | ("binary literal expression statement", 89 | "true;", 90 | expected); 91 | } 92 | 93 | { 94 | const auto expected 95 | (new BlockStatement 96 | {new ExpressionStatement 97 | (new IntegerValue(1234))}); 98 | TEST_PARSE 99 | ("integer literal expression statement", 100 | "1234;", 101 | expected); 102 | } 103 | 104 | { 105 | const auto expected 106 | (new BlockStatement 107 | {new ExpressionStatement 108 | (new BinaryExpression 109 | (binary::operators["+"], 110 | new IntegerValue(12), 111 | new IntegerValue(34)))}); 112 | TEST_PARSE 113 | ("simple arithmetic expression statement", 114 | "12+34;", 115 | expected); 116 | } 117 | 118 | { 119 | const auto expected 120 | (new BlockStatement 121 | {new ExpressionStatement 122 | (new CallExpression 123 | (new IdentifierExpression("f"), 124 | {}))}); 125 | TEST_PARSE 126 | ("call expression statement with no arguments", 127 | "f();", 128 | expected); 129 | } 130 | 131 | { 132 | const auto expected 133 | (new BlockStatement 134 | {new ExpressionStatement 135 | (new CallExpression 136 | (new IdentifierExpression("f"), 137 | {new IdentifierExpression("x"), 138 | new IdentifierExpression("y")}))}); 139 | TEST_PARSE 140 | ("call expression statement with arguments", 141 | "f(x, y);", 142 | expected); 143 | } 144 | 145 | { 146 | const auto expected 147 | (new BlockStatement 148 | {new ExpressionStatement 149 | (new SubscriptExpression 150 | (new CallExpression 151 | (new IdentifierExpression("f"), 152 | {new IdentifierExpression("x"), 153 | new IdentifierExpression("y")}), 154 | new IdentifierExpression("z")))}); 155 | TEST_PARSE 156 | ("subscripted parenthesized call expression statement", 157 | "(f(x, y))[z];", 158 | expected); 159 | } 160 | 161 | { 162 | const auto expected 163 | (new BlockStatement 164 | {new ExpressionStatement 165 | (new CallExpression 166 | (new SubscriptExpression 167 | (new IdentifierExpression("f"), 168 | new IdentifierExpression("z")), 169 | {new IdentifierExpression("x"), 170 | new IdentifierExpression("y")}))}); 171 | TEST_PARSE 172 | ("called subscript expression statement", 173 | "f[z](x, y);", 174 | expected); 175 | } 176 | 177 | { 178 | const auto expected 179 | (new BlockStatement 180 | {new ExpressionStatement 181 | (new SubscriptExpression 182 | (new IdentifierExpression("things"), 183 | new IdentifierExpression("index")))}); 184 | TEST_PARSE 185 | ("subscript expression statement", 186 | "things[index];", 187 | expected); 188 | } 189 | 190 | { 191 | const auto expected 192 | (new BlockStatement 193 | {new VarStatement 194 | ("x", 195 | new UndefinedValue())}); 196 | TEST_PARSE 197 | ("variable declaration without initializer", 198 | "var x;", 199 | expected); 200 | } 201 | 202 | { 203 | const auto expected 204 | (new BlockStatement 205 | {new VarStatement 206 | ("x", 207 | new IntegerValue(50))}); 208 | TEST_PARSE 209 | ("variable declaration with literal initializer", 210 | "var x = 50;", 211 | expected); 212 | } 213 | 214 | { 215 | const auto expected 216 | (new BlockStatement 217 | {new VarStatement 218 | ("x", 219 | new BinaryExpression 220 | (binary::operators["+"], 221 | new IdentifierExpression("y"), 222 | new IdentifierExpression("z")))}); 223 | TEST_PARSE 224 | ("variable declaration with expression initializer", 225 | "var x = y + z;", 226 | expected); 227 | } 228 | 229 | { 230 | const auto expected 231 | (new BlockStatement 232 | {new DelStatement("x")}); 233 | TEST_PARSE 234 | ("variable deletion", 235 | "del x;", 236 | expected); 237 | } 238 | 239 | { 240 | const auto expected 241 | (new BlockStatement 242 | {new BlockStatement 243 | {new ExpressionStatement 244 | (new CallExpression 245 | (new IdentifierExpression("f"), 246 | {})), 247 | new ExpressionStatement 248 | (new CallExpression 249 | (new IdentifierExpression("g"), 250 | {})), 251 | new ExpressionStatement 252 | (new CallExpression 253 | (new IdentifierExpression("h"), 254 | {}))}}); 255 | TEST_PARSE 256 | ("block statement with call expression statements", 257 | "{\n" 258 | "\tf();\n" 259 | "\tg();\n" 260 | "\th();\n" 261 | "}\n", 262 | expected); 263 | } 264 | 265 | { 266 | const auto expected 267 | (new BlockStatement 268 | {new IfStatement 269 | (new CallExpression 270 | (new IdentifierExpression("condition"), 271 | {new IdentifierExpression("x")}), 272 | new BlockStatement 273 | {new ExpressionStatement 274 | (new CallExpression 275 | (new IdentifierExpression("handler"), 276 | {new IdentifierExpression("x")}))})}); 277 | TEST_PARSE 278 | ("variable declaration with expression initializer", 279 | "if (condition(x)) {\n" 280 | "\thandler(x);\n" 281 | "}\n", 282 | expected); 283 | } 284 | 285 | { 286 | const auto expected 287 | (new BlockStatement 288 | {new FunStatement 289 | ("f", 290 | {"x", "y"}, 291 | new RetStatement 292 | (new BinaryExpression 293 | (binary::operators["+"], 294 | new IdentifierExpression("x"), 295 | new IdentifierExpression("y"))))}); 296 | TEST_PARSE 297 | ("function declaration", 298 | "fun f(x, y)\n" 299 | " ret x + y;", 300 | expected); 301 | } 302 | 303 | { 304 | const auto expected 305 | (new BlockStatement 306 | {new NextStatement()}); 307 | TEST_PARSE 308 | ("next statement", 309 | "next;", 310 | expected); 311 | } 312 | 313 | { 314 | const auto expected 315 | (new BlockStatement 316 | {new LastStatement()}); 317 | TEST_PARSE 318 | ("last statement", 319 | "last;", 320 | expected); 321 | } 322 | 323 | { 324 | const auto expected 325 | (new BlockStatement 326 | {new ExitStatement()}); 327 | TEST_PARSE 328 | ("exit statement", 329 | "exit;", 330 | expected); 331 | } 332 | 333 | { 334 | const auto expected 335 | (new BlockStatement 336 | {new RedoStatement()}); 337 | TEST_PARSE 338 | ("redo statement", 339 | "redo;", 340 | expected); 341 | } 342 | 343 | { 344 | const auto expected 345 | (new BlockStatement 346 | {new ExpressionStatement 347 | (new FunValue 348 | ("lambda", 349 | {"x", "y"}, 350 | new RetStatement 351 | (new BinaryExpression 352 | (binary::operators["+"], 353 | new IdentifierExpression("x"), 354 | new IdentifierExpression("y")))))}); 355 | TEST_PARSE 356 | ("anonymous lambda with expression", 357 | "(lam (x, y): x + y);", 358 | expected); 359 | } 360 | 361 | { 362 | const auto expected 363 | (new BlockStatement 364 | {new ForStatement 365 | (new VarStatement 366 | ("x", 367 | new IntegerValue(0)), 368 | new BinaryExpression 369 | (binary::operators["<"], 370 | new IdentifierExpression("x"), 371 | new IntegerValue(10)), 372 | new BinaryExpression 373 | (binary::operators["="], 374 | new IdentifierExpression("x"), 375 | new BinaryExpression 376 | (binary::operators["+"], 377 | new IdentifierExpression("x"), 378 | new IntegerValue(1))), 379 | new BlockStatement 380 | {})}); 381 | TEST_PARSE 382 | ("for loop", 383 | "for (var x = 0; x < 10; x = x + 1) {}", 384 | expected); 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /unit/tokenize.cpp: -------------------------------------------------------------------------------- 1 | #include "tokenize.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace hap; 8 | using namespace std; 9 | 10 | #define TEST_TOKENIZE(NAME, INPUT, EXPECTED) \ 11 | test_tokenize(__FILE__, __LINE__, NAME, INPUT, EXPECTED) 12 | 13 | void test_tokenize 14 | (const string& file, 15 | const int line, 16 | const string& name, 17 | const string& input, 18 | const vector& expected) { 19 | istringstream stream(input); 20 | const auto actual(tokenize(stream)); 21 | if (actual == expected) 22 | return; 23 | ostringstream message; 24 | message 25 | << file << ":" << line 26 | << ": Unit test '" << name << "' failed:\n" 27 | << "\texpected: "; 28 | copy(begin(expected), end(expected), 29 | ostream_iterator(message, " ")); 30 | message << "\n\t actual: "; 31 | copy(begin(actual), end(actual), 32 | ostream_iterator(message, " ")); 33 | message << '\n'; 34 | throw runtime_error(message.str()); 35 | } 36 | 37 | void suite_tokenize() { 38 | { 39 | vector expected; 40 | TEST_TOKENIZE 41 | ("empty input", 42 | "", 43 | expected); 44 | } 45 | 46 | { 47 | vector expected; 48 | TEST_TOKENIZE 49 | ("only spaces", 50 | " ", 51 | expected); 52 | } 53 | 54 | { 55 | vector expected; 56 | TEST_TOKENIZE 57 | ("all whitespace characters", 58 | " \t\n\r\f\v", 59 | expected); 60 | } 61 | 62 | { 63 | vector expected; 64 | TEST_TOKENIZE 65 | ("comment ending at end of input", 66 | "# this is a comment", 67 | expected); 68 | } 69 | 70 | { 71 | vector expected; 72 | TEST_TOKENIZE 73 | ("comment with newline", 74 | "# this is a comment\n", 75 | expected); 76 | } 77 | 78 | { 79 | vector expected{Token(Token::LEFT_PARENTHESIS, "(")}; 80 | TEST_TOKENIZE 81 | ("left parenthesis", 82 | "(", 83 | expected); 84 | } 85 | 86 | { 87 | vector expected{Token(Token::RIGHT_PARENTHESIS, ")")}; 88 | TEST_TOKENIZE 89 | ("right parenthesis", 90 | ")", 91 | expected); 92 | } 93 | 94 | { 95 | vector expected{Token(Token::LEFT_BRACE, "{")}; 96 | TEST_TOKENIZE 97 | ("left brace", 98 | "{", 99 | expected); 100 | } 101 | 102 | { 103 | vector expected{Token(Token::RIGHT_BRACE, "}")}; 104 | TEST_TOKENIZE 105 | ("right brace", 106 | "}", 107 | expected); 108 | } 109 | 110 | { 111 | vector expected{Token(Token::LEFT_BRACKET, "[")}; 112 | TEST_TOKENIZE 113 | ("left bracket", 114 | "[", 115 | expected); 116 | } 117 | 118 | { 119 | vector expected{Token(Token::RIGHT_BRACKET, "]")}; 120 | TEST_TOKENIZE 121 | ("right bracket", 122 | "]", 123 | expected); 124 | } 125 | 126 | { 127 | vector expected{Token(Token::COMMA, ",")}; 128 | TEST_TOKENIZE 129 | ("comma", 130 | ",", 131 | expected); 132 | } 133 | 134 | { 135 | vector expected{Token(Token::SEMICOLON, ";")}; 136 | TEST_TOKENIZE 137 | ("semicolon", 138 | ";", 139 | expected); 140 | } 141 | 142 | { 143 | vector expected{Token(Token::COLON, ":")}; 144 | TEST_TOKENIZE 145 | ("colon", 146 | ":", 147 | expected); 148 | } 149 | 150 | { 151 | vector expected{Token(Token::STRING, "\"\"")}; 152 | TEST_TOKENIZE 153 | ("empty string literal", 154 | "\"\"", 155 | expected); 156 | } 157 | 158 | { 159 | vector expected{Token(Token::STRING, "\"test\"")}; 160 | TEST_TOKENIZE 161 | ("string literal", 162 | "\"test\"", 163 | expected); 164 | } 165 | 166 | { 167 | vector expected{Token(Token::STRING, "\"\a\b\f\n\r\t\v\"\\\"")}; 168 | TEST_TOKENIZE 169 | ("string literal with escapes", 170 | "\"\\a\\b\\f\\n\\r\\t\\v\\\"\\\\\"", 171 | expected); 172 | } 173 | 174 | { 175 | vector expected{Token(Token::INTEGER, "0")}; 176 | TEST_TOKENIZE 177 | ("zero", 178 | "0", 179 | expected); 180 | } 181 | 182 | { 183 | vector expected{Token(Token::INTEGER, "1")}; 184 | TEST_TOKENIZE 185 | ("single-digit integer", 186 | "1", 187 | expected); 188 | } 189 | 190 | { 191 | vector expected{Token(Token::INTEGER, "12345")}; 192 | TEST_TOKENIZE 193 | ("multiple-digit integer", 194 | "12345", 195 | expected); 196 | } 197 | 198 | { 199 | vector expected{Token(Token::FLOAT, "0.0")}; 200 | TEST_TOKENIZE 201 | ("floating-point zero", 202 | "0.0", 203 | expected); 204 | } 205 | 206 | { 207 | vector expected{Token(Token::FLOAT, "1.0")}; 208 | TEST_TOKENIZE 209 | ("floating-point one", 210 | "1.0", 211 | expected); 212 | } 213 | 214 | { 215 | vector expected{Token(Token::FLOAT, "1234.5678")}; 216 | TEST_TOKENIZE 217 | ("floating-point number", 218 | "1234.5678", 219 | expected); 220 | } 221 | 222 | { 223 | vector expected{Token(Token::IDENTIFIER, "_")}; 224 | TEST_TOKENIZE 225 | ("underscore", 226 | "_", 227 | expected); 228 | } 229 | 230 | { 231 | vector expected{Token(Token::IDENTIFIER, "abc")}; 232 | TEST_TOKENIZE 233 | ("lowercase alphabetic identifier", 234 | "abc", 235 | expected); 236 | } 237 | 238 | { 239 | vector expected{Token(Token::IDENTIFIER, "abc123")}; 240 | TEST_TOKENIZE 241 | ("lowercase alphanumeric identifier", 242 | "abc123", 243 | expected); 244 | } 245 | 246 | { 247 | vector expected{Token(Token::IDENTIFIER, "Abc123")}; 248 | TEST_TOKENIZE 249 | ("mixed-case alphanumeric identifier", 250 | "Abc123", 251 | expected); 252 | } 253 | 254 | { 255 | vector expected{Token(Token::IDENTIFIER, "abc_123")}; 256 | TEST_TOKENIZE 257 | ("alphanumeric identifier with underscores", 258 | "abc_123", 259 | expected); 260 | } 261 | 262 | { 263 | vector expected 264 | {Token(Token::IDENTIFIER, "abc"), 265 | Token(Token::IDENTIFIER, "_123"), 266 | Token(Token::IDENTIFIER, "abc_123"), 267 | Token(Token::IDENTIFIER, "Abc123"), 268 | Token(Token::IDENTIFIER, "ABC")}; 269 | TEST_TOKENIZE 270 | ("whitespace-separated identifiers", 271 | "abc _123\tabc_123\n Abc123\n\tABC", 272 | expected); 273 | } 274 | 275 | { 276 | vector expected{Token(Token::OPERATOR, "+")}; 277 | TEST_TOKENIZE 278 | ("single-character operator", 279 | "+", 280 | expected); 281 | } 282 | 283 | { 284 | vector expected{Token(Token::OPERATOR, "!!!")}; 285 | TEST_TOKENIZE 286 | ("multi-character operator", 287 | "!!!", 288 | expected); 289 | } 290 | 291 | { 292 | vector expected{Token(Token::OPERATOR, "!$%&*+-./<=>?@\\^|~")}; 293 | TEST_TOKENIZE 294 | ("all operator characters", 295 | "!$%&*+-./<=>?@\\^|~", 296 | expected); 297 | } 298 | 299 | { 300 | vector expected 301 | {Token(Token::OPERATOR, "+"), 302 | Token(Token::COMMA, ","), 303 | Token(Token::OPERATOR, "-"), 304 | Token(Token::SEMICOLON, ";"), 305 | Token(Token::DOT, "."), 306 | Token(Token::DOT, "."), 307 | Token(Token::DOT, "."), 308 | Token(Token::COLON, ":"), 309 | Token(Token::OPERATOR, "?")}; 310 | TEST_TOKENIZE 311 | ("mixed operators and single-character tokens", 312 | "+,-;...:?", 313 | expected); 314 | } 315 | 316 | { 317 | vector expected 318 | {Token(Token::IDENTIFIER, "abc"), 319 | Token(Token::STRING, "\"abc\"")}; 320 | TEST_TOKENIZE 321 | ("identifier followed by string literal", 322 | "abc\"abc\"", 323 | expected); 324 | } 325 | 326 | { 327 | vector expected 328 | {Token(Token::STRING, "\"abc\""), 329 | Token(Token::IDENTIFIER, "abc")}; 330 | TEST_TOKENIZE 331 | ("string literal followed by identifier", 332 | "\"abc\"abc", 333 | expected); 334 | } 335 | 336 | { 337 | vector expected 338 | {Token(Token::INTEGER, "123"), 339 | Token(Token::IDENTIFIER, "abc")}; 340 | TEST_TOKENIZE 341 | ("integer followed by identifier", 342 | "123abc", 343 | expected); 344 | } 345 | 346 | { 347 | vector expected 348 | {Token(Token::FLOAT, "0.5"), 349 | Token(Token::IDENTIFIER, "abc")}; 350 | TEST_TOKENIZE 351 | ("float followed by identifier", 352 | "0.5abc", 353 | expected); 354 | } 355 | } 356 | --------------------------------------------------------------------------------