//: Advanced notation for the common/easy case where a recipe takes some fixed //: number of ingredients and yields some fixed number of products. :(scenario recipe_with_header) def main [ 1:number/raw <- add2 3, 5 ] def add2 x:number, y:number -> z:number [ local-scope load-ingredients z:number <- add x, y return z ] +mem: storing 8 in location 1 //: When loading recipes save any header. :(before "End recipe Fields") bool has_header; vector ingredients; vector products; :(before "End recipe Constructor") has_header = false; :(before "End Recipe Refinements") if (in.peek() != '[') { trace(9999, "parse") << "recipe has a header; parsing" << end(); load_recipe_header(in, result); } :(code) void load_recipe_header(istream& in, recipe& result) { result.has_header = true; while (has_data(in) && in.peek() != '[' && in.peek() != '\n') { string s = next_word(in); if (s == "<-") raise << "recipe " << result.name << " should say '->' and not '<-'\n" << end(); if (s == "->") break; result.ingredients.push_back(reagent(s)); trace(9999, "parse") << "header ingredient: " << result.ingredients.back().original_string << end(); skip_whitespace_but_not_newline(in); } while (has_data(in) && in.peek() != '[' && in.peek() != '\n') { string s = next_word(in); result.products.push_back(reagent(s)); trace(9999, "parse") << "header product: " << result.products.back().original_string << end(); skip_whitespace_but_not_newline(in); } // End Load Recipe Header(result) } :(scenario recipe_handles_stray_comma) def main [ 1:number/raw <- add2 3, 5 ] def add2 x:number, y:number -> z:number, [ local-scope load-ingredients z:number <- add x, y return z ] +mem: storing 8 in location 1 :(scenario recipe_handles_stray_comma_2) def main [ foo ] def foo, [ 1:number/raw <- add 2, 2 ] def bar [ 1:number/raw <- add 2, 3 ] +mem: storing 4 in location 1 :(scenario recipe_handles_wrong_arrow) % Hide_errors = true; def foo a:number <- b:number [ ] +error: recipe foo should say '->' and not '<-' :(scenario recipe_handles_missing_bracket) % Hide_errors = true; def main ] +error: recipe body must begin with '[' :(scenario recipe_handles_missing_bracket_2) % Hide_errors = true; def main local-scope { } ] # doesn't overflow line when reading header -parse: header ingredient: local-scope +error: recipe body must begin with '[' :(scenario recipe_handles_missing_bracket_3) % Hide_errors = true; def main # comment local-scope { } ] # doesn't overflow line when reading header -parse: header ingredient: local-scope +error: recipe body must begin with '[' :(after "Begin debug_string(recipe x)") out << "ingredients:\n"; for (int i = 0; i < SIZE(x.ingredients); ++i) out << " " << debug_string(x.ingredients.at(i)) << '\n'; out << "products:\n"; for (int i = 0; i < SIZE(x.products); ++i) out << " " << debug_string(x.products.at(i)) << '\n'; //: If a recipe never mentions any ingredients or products, assume it has a header. :(scenario recipe_without_ingredients_or_products_has_header) def test [ 1:number <- copy 34 ] +parse: recipe test has a header :(before "End Recipe Body(result)") if (!result.has_header) { result.has_header = true; for (int i = 0; i < SIZE(result.steps); ++i) { const instruction& inst = result.steps.at(i); if ((inst.name == "reply" && !inst.ingredients.empty()) || (inst.name == "return" && !inst.ingredients.empty()) || inst.name == "next-ingredient" || inst.name == "ingredient" || inst.name == "rewind-ingredients") { result.has_header = false; break; } } } if (result.has_header) { trace(9999, "parse") << "recipe " << result.name << " has a header" << end(); } //: Rewrite 'load-ingredients' to instructions to create all reagents in the header. :(before "End Rewrite Instruction(curr, recipe result)") if (curr.name == "load-ingredients") { curr.clear(); recipe_ordinal op = get(Recipe_ordinal, "next-ingredient-without-typechecking"); for (int i = 0; i < SIZE(result.ingredients); ++i) { curr.operation = op; curr.name = "next-ingredient-without-typechecking"; curr.products.push_back(result.ingredients.at(i)); result.steps.push_back(curr); curr.clear(); } } //: internal version of next-ingredient; don't call this directly :(before "End Primitive Recipe Declarations") NEXT_INGREDIENT_WITHOUT_TYPECHECKING, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "next-ingredient-without-typechecking", NEXT_INGREDIENT_WITHOUT_TYPECHECKING); :(before "End Primitive Recipe Checks") case NEXT_INGREDIENT_WITHOUT_TYPECHECKING: { break; } :(before "End Primitive Recipe Implementations") case NEXT_INGREDIENT_WITHOUT_TYPECHECKING: { assert(!Current_routine->calls.empty()); if (current_call().next_ingredient_to_process < SIZE(current_call().ingredient_atoms)) { products.push_back( current_call().ingredient_atoms.at(current_call().next_ingredient_to_process)); assert(SIZE(products) == 1); products.resize(2); // push a new vector products.at(1).push_back(1); ++current_call().next_ingredient_to_process; } else { products.resize(2); // pad the first product with sufficient zeros to match its type int size = size_of(current_instruction().products.at(0)); for (int i = 0; i < size; ++i) products.at(0).push_back(0); products.at(1).push_back(0); } break; } //:: Check all calls against headers. :(scenario show_clear_error_on_bad_call) % Hide_errors = true; def main [ 1:number <- foo 34 ] def foo x:point -> y:number [ local-scope load-ingredients return 35 ] +error: main: ingredient 0 has the wrong type at '1:nu
# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

if __name__ == '__main__': from __init__ import init; init()

import unittest
from ranger.ext.direction import Direction
from ranger.ext.openstruct import OpenStruct

class TestDirections(unittest.TestCase):
	def test_symmetry(self):
		d1 = Direction(right=4, down=7, relative=True)
		d2 = Direction(left=-4, up=-7, absolute=False)

		def subtest(d):
			self.assertEqual(4, d.right())
			self.assertEqual(7, d.down())
			self.assertEqual(-4, d.left())
			self.assertEqual(-7, d.up())
			self.assertEqual(True, d.relative())
			self.assertEqual(False, d.absolute())

			self.assertTrue(d.horizontal())
			self.assertTrue(d.vertical())

		subtest(d1)
		subtest(d2)

	def test_conflicts(self):
		d3