Merge remote-tracking branch 'origin/master' into c++11

This commit is contained in:
Marius Kintel 2016-01-20 00:42:19 -05:00
commit f2b5382247
17 changed files with 314 additions and 126 deletions

View file

@ -1,6 +1,14 @@
#include <exception>
#pragma once
class RecursionException: public std::runtime_error {
#include <stdexcept>
class EvaluationException : public std::runtime_error {
public:
EvaluationException(const std::string &what_arg) : std::runtime_error(what_arg) {}
virtual ~EvaluationException() throw() {}
};
class RecursionException: public EvaluationException {
public:
static RecursionException create(const char *recursiontype, const std::string &name) {
std::stringstream out;
@ -10,5 +18,5 @@ public:
virtual ~RecursionException() throw() {}
private:
RecursionException(const std::string &what_arg) : std::runtime_error(what_arg) {}
RecursionException(const std::string &what_arg) : EvaluationException(what_arg) {}
};

View file

@ -33,6 +33,7 @@
#include "printutils.h"
#include "stackcheck.h"
#include "exceptions.h"
#include "feature.h"
#include <boost/bind.hpp>
// unnamed namespace
@ -409,7 +410,14 @@ ValuePtr ExpressionVector::evaluate(const Context *context) const
Value::VectorType vec;
for(const auto &e : this->children) {
ValuePtr tmpval = e->evaluate(context);
vec.push_back(tmpval);
if (e->isListComprehension()) {
const Value::VectorType result = tmpval->toVector();
for (size_t i = 0;i < result.size();i++) {
vec.push_back(result[i]);
}
} else {
vec.push_back(tmpval);
}
}
return ValuePtr(vec);
}
@ -505,29 +513,12 @@ void ExpressionLet::print(std::ostream &stream) const
stream << "let(" << this->call_arguments << ") " << *first;
}
ExpressionLcExpression::ExpressionLcExpression(Expression *expr) : Expression(expr)
ExpressionLc::ExpressionLc(Expression *expr) : Expression(expr)
{
}
ValuePtr ExpressionLcExpression::evaluate(const Context *context) const
{
return this->first->evaluate(context);
}
void ExpressionLcExpression::print(std::ostream &stream) const
{
stream << "[" << *this->first << "]";
}
ExpressionLc::ExpressionLc(const std::string &name,
const AssignmentList &arglist, Expression *expr)
: Expression(expr), name(name), call_arguments(arglist)
{
}
ExpressionLc::ExpressionLc(const std::string &name,
Expression *expr1, Expression *expr2)
: Expression(expr1, expr2), name(name)
ExpressionLc::ExpressionLc(Expression *expr1, Expression *expr2)
: Expression(expr1, expr2)
{
}
@ -536,78 +527,150 @@ bool ExpressionLc::isListComprehension() const
return true;
}
ValuePtr ExpressionLc::evaluate(const Context *context) const
ExpressionLcIf::ExpressionLcIf(Expression *cond, Expression *exprIf, Expression *exprElse)
: ExpressionLc(exprIf, exprElse), cond(cond)
{
}
ValuePtr ExpressionLcIf::evaluate(const Context *context) const
{
if (this->second) {
ExperimentalFeatureException::check(Feature::ExperimentalElseExpression);
}
const Expression *expr = this->cond->evaluate(context) ? this->first : this->second;
Value::VectorType vec;
if (expr) {
if (expr->isListComprehension()) {
return expr->evaluate(context);
} else {
vec.push_back(expr->evaluate(context));
}
}
return ValuePtr(vec);
}
void ExpressionLcIf::print(std::ostream &stream) const
{
stream << "if(" << *this->cond << ") (" << *this->first << ")";
if (this->second) {
stream << " else (" << *this->second << ")";
}
}
ExpressionLcEach::ExpressionLcEach(Expression *expr)
: ExpressionLc(expr)
{
}
ValuePtr ExpressionLcEach::evaluate(const Context *context) const
{
ExperimentalFeatureException::check(Feature::ExperimentalEachExpression);
Value::VectorType vec;
ValuePtr v = this->first->evaluate(context);
if (v->type() == Value::RANGE) {
RangeType range = v->toRange();
boost::uint32_t steps = range.numValues();
if (steps >= 1000000) {
PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps);
} else {
for (RangeType::iterator it = range.begin();it != range.end();it++) {
vec.push_back(ValuePtr(*it));
}
}
} else if (v->type() == Value::VECTOR) {
Value::VectorType vector = v->toVector();
for (size_t i = 0; i < v->toVector().size(); i++) {
vec.push_back(vector[i]);
}
} else if (v->type() != Value::UNDEFINED) {
vec.push_back(v);
}
if (this->first->isListComprehension()) {
return ValuePtr(flatten(vec));
} else {
return ValuePtr(vec);
}
}
void ExpressionLcEach::print(std::ostream &stream) const
{
stream << "each (" << *this->first << ")";
}
ExpressionLcFor::ExpressionLcFor(const AssignmentList &arglist, Expression *expr)
: ExpressionLc(expr), call_arguments(arglist)
{
}
ValuePtr ExpressionLcFor::evaluate(const Context *context) const
{
Value::VectorType vec;
if (this->name == "if") {
if (this->first->evaluate(context)) {
if (this->second->isListComprehension()) {
return this->second->evaluate(context);
} else {
vec.push_back(this->second->evaluate(context));
}
}
return ValuePtr(vec);
} else if (this->name == "for") {
EvalContext for_context(context, this->call_arguments);
EvalContext for_context(context, this->call_arguments);
Context assign_context(context);
Context assign_context(context);
// comprehension for statements are by the parser reduced to only contain one single element
const std::string &it_name = for_context.getArgName(0);
ValuePtr it_values = for_context.getArgValue(0, &assign_context);
// comprehension for statements are by the parser reduced to only contain one single element
const std::string &it_name = for_context.getArgName(0);
ValuePtr it_values = for_context.getArgValue(0, &assign_context);
Context c(context);
Context c(context);
if (it_values->type() == Value::RANGE) {
RangeType range = it_values->toRange();
boost::uint32_t steps = range.numValues();
if (steps >= 1000000) {
PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps);
} else {
for (RangeType::iterator it = range.begin();it != range.end();it++) {
c.set_variable(it_name, ValuePtr(*it));
vec.push_back(this->first->evaluate(&c));
}
}
}
else if (it_values->type() == Value::VECTOR) {
for (size_t i = 0; i < it_values->toVector().size(); i++) {
c.set_variable(it_name, it_values->toVector()[i]);
vec.push_back(this->first->evaluate(&c));
}
}
else if (it_values->type() != Value::UNDEFINED) {
c.set_variable(it_name, it_values);
vec.push_back(this->first->evaluate(&c));
}
if (this->first->isListComprehension()) {
return ValuePtr(flatten(vec));
} else {
return ValuePtr(vec);
}
} else if (this->name == "let") {
Context c(context);
evaluate_sequential_assignment(this->call_arguments, &c);
if (it_values->type() == Value::RANGE) {
RangeType range = it_values->toRange();
boost::uint32_t steps = range.numValues();
if (steps >= 1000000) {
PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps);
} else {
for (RangeType::iterator it = range.begin();it != range.end();it++) {
c.set_variable(it_name, ValuePtr(*it));
vec.push_back(this->first->evaluate(&c));
}
}
} else if (it_values->type() == Value::VECTOR) {
for (size_t i = 0; i < it_values->toVector().size(); i++) {
c.set_variable(it_name, it_values->toVector()[i]);
vec.push_back(this->first->evaluate(&c));
}
} else if (it_values->type() != Value::UNDEFINED) {
c.set_variable(it_name, it_values);
vec.push_back(this->first->evaluate(&c));
}
return this->first->evaluate(&c);
} else {
abort();
}
if (this->first->isListComprehension()) {
return ValuePtr(flatten(vec));
} else {
return ValuePtr(vec);
}
}
void ExpressionLc::print(std::ostream &stream) const
void ExpressionLcFor::print(std::ostream &stream) const
{
stream << this->name;
if (this->name == "if") {
stream << "(" << *this->first << ") " << *this->second;
}
else if (this->name == "for" || this->name == "let") {
stream << "(" << this->call_arguments << ") " << *this->first;
} else {
assert(false && "Illegal list comprehension element");
}
stream << "for(" << this->call_arguments << ") (" << *this->first << ")";
}
ExpressionLcLet::ExpressionLcLet(const AssignmentList &arglist, Expression *expr)
: ExpressionLc(expr), call_arguments(arglist)
{
}
ValuePtr ExpressionLcLet::evaluate(const Context *context) const
{
Context c(context);
evaluate_sequential_assignment(this->call_arguments, &c);
return this->first->evaluate(&c);
}
void ExpressionLcLet::print(std::ostream &stream) const
{
stream << "let(" << this->call_arguments << ") (" << *this->first << ")";
}
std::ostream &operator<<(std::ostream &stream, const Expression &expr)

View file

@ -231,25 +231,48 @@ private:
AssignmentList call_arguments;
};
class ExpressionLcExpression : public Expression
{
public:
ExpressionLcExpression(Expression *expr);
ValuePtr evaluate(const class Context *context) const;
virtual void print(std::ostream &stream) const;
};
class ExpressionLc : public Expression
{
virtual bool isListComprehension() const;
public:
ExpressionLc(const std::string &name,
const AssignmentList &arglist, Expression *expr);
ExpressionLc(const std::string &name,
Expression *expr1, Expression *expr2);
ExpressionLc(Expression *expr);
ExpressionLc(Expression *expr1, Expression *expr2);
};
class ExpressionLcIf : public ExpressionLc
{
public:
ExpressionLcIf(Expression *cond, Expression *exprIf, Expression *exprElse);
ValuePtr evaluate(const class Context *context) const;
virtual void print(std::ostream &stream) const;
private:
Expression *cond;
};
class ExpressionLcFor : public ExpressionLc
{
public:
ExpressionLcFor(const AssignmentList &arglist, Expression *expr);
ValuePtr evaluate(const class Context *context) const;
virtual void print(std::ostream &stream) const;
private:
AssignmentList call_arguments;
};
class ExpressionLcEach : public ExpressionLc
{
public:
ExpressionLcEach(Expression *expr);
ValuePtr evaluate(const class Context *context) const;
virtual void print(std::ostream &stream) const;
};
class ExpressionLcLet : public ExpressionLc
{
public:
ExpressionLcLet(const AssignmentList &arglist, Expression *expr);
ValuePtr evaluate(const class Context *context) const;
virtual void print(std::ostream &stream) const;
private:
std::string name;
AssignmentList call_arguments;
};

View file

@ -1,5 +1,6 @@
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <string>
#include <map>
@ -18,6 +19,8 @@ Feature::list_t Feature::feature_list;
* argument to enable the option and for saving the option value in GUI
* context.
*/
const Feature Feature::ExperimentalEachExpression("lc-each", "Enable <code>each</code> expression in list comprehensions.");
const Feature Feature::ExperimentalElseExpression("lc-else", "Enable <code>else</code> expression in list comprehensions.");
Feature::Feature(const std::string &name, const std::string &description)
: enabled(false), name(name), description(description)
@ -76,3 +79,23 @@ void Feature::dump_features()
std::cout << "Feature('" << it->first << "') = " << (it->second->is_enabled() ? "enabled" : "disabled") << std::endl;
}
}
ExperimentalFeatureException::ExperimentalFeatureException(const std::string &what_arg)
: EvaluationException(what_arg)
{
}
ExperimentalFeatureException::~ExperimentalFeatureException() throw()
{
}
void ExperimentalFeatureException::check(const Feature &feature)
{
if (!feature.is_enabled()) {
std::stringstream out;
out << "ERROR: Experimental feature not enabled: '" << feature.get_name() << "'. Please check preferences.";
throw ExperimentalFeatureException(out.str());
}
}

View file

@ -6,12 +6,17 @@
#include <map>
#include <vector>
#include "exceptions.h"
class Feature
{
public:
typedef std::vector<Feature *> list_t;
typedef list_t::iterator iterator;
static const Feature ExperimentalEachExpression;
static const Feature ExperimentalElseExpression;
const std::string& get_name() const;
const std::string& get_description() const;
@ -37,3 +42,13 @@ private:
Feature(const std::string &name, const std::string &description);
virtual ~Feature();
};
class ExperimentalFeatureException : public EvaluationException
{
public:
static void check(const Feature &feature);
virtual ~ExperimentalFeatureException() throw();
private:
ExperimentalFeatureException(const std::string &what_arg);
};

View file

@ -219,7 +219,7 @@ Highlighter::Highlighter(QTextDocument *parent)
{
tokentypes["operator"] << "=" << "!" << "&&" << "||" << "+" << "-" << "*" << "/" << "%" << "!" << "#" << ";";
tokentypes["math"] << "abs" << "sign" << "acos" << "asin" << "atan" << "atan2" << "sin" << "cos" << "floor" << "round" << "ceil" << "ln" << "log" << "lookup" << "min" << "max" << "pow" << "sqrt" << "exp" << "rands";
tokentypes["keyword"] << "module" << "function" << "for" << "intersection_for" << "if" << "assign" << "echo"<< "search" << "str" << "let";
tokentypes["keyword"] << "module" << "function" << "for" << "intersection_for" << "if" << "assign" << "echo"<< "search" << "str" << "let" << "each";
tokentypes["transform"] << "scale" << "translate" << "rotate" << "multmatrix" << "color" << "projection" << "hull" << "resize" << "mirror" << "minkowski";
tokentypes["csgop"] << "union" << "intersection" << "difference" << "render";
tokentypes["prim3d"] << "cube" << "cylinder" << "sphere" << "polyhedron";

View file

@ -152,6 +152,7 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
"else" return TOK_ELSE;
"let" return TOK_LET;
"for" return TOK_FOR;
"each" return TOK_EACH;
"true" return TOK_TRUE;
"false" return TOK_FALSE;

View file

@ -354,7 +354,7 @@ AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiat
std::vector<AbstractNode *> instantiatednodes = this->scope.instantiateChildren(context);
node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end());
}
catch (RecursionException &e) {
catch (EvaluationException &e) {
PRINT(e.what());
}

View file

@ -24,7 +24,7 @@
*
*/
%expect 1 /* Expect 1 shift/reduce conflict for ifelse_statement - "dangling else problem" */
%expect 2 /* Expect 2 shift/reduce conflict for ifelse_statement - "dangling else problem" */
%{
@ -89,6 +89,7 @@ std::string parser_source_path;
%token TOK_ELSE
%token TOK_FOR
%token TOK_LET
%token TOK_EACH
%token <text> TOK_ID
%token <text> TOK_STRING
@ -119,6 +120,7 @@ std::string parser_source_path;
%type <expr> expr
%type <expr> vector_expr
%type <expr> list_comprehension_elements
%type <expr> list_comprehension_elements_p
%type <expr> list_comprehension_elements_or_expr
%type <inst> module_instantiation
@ -284,10 +286,11 @@ child_statement:
}
;
// "for" is a valid module identifier
// "for" and "each" are a valid module identifier
module_id:
TOK_ID { $$ = $1; }
| TOK_FOR { $$ = strdup("for"); }
| TOK_EACH { $$ = strdup("each"); }
;
single_module_instantiation:
@ -346,10 +349,6 @@ expr:
{
$$ = new ExpressionRange($2, $4, $6);
}
| '[' list_comprehension_elements ']'
{
$$ = new ExpressionLcExpression($2);
}
| '[' optional_commas ']'
{
$$ = new ExpressionConst(ValuePtr(Value::VectorType()));
@ -445,11 +444,15 @@ expr:
list_comprehension_elements:
/* The last set element may not be a "let" (as that would instead
be parsed as an expression) */
TOK_LET '(' arguments_call ')' list_comprehension_elements
TOK_LET '(' arguments_call ')' list_comprehension_elements_p
{
$$ = new ExpressionLc("let", *$3, $5);
$$ = new ExpressionLcLet(*$3, $5);
delete $3;
}
| TOK_EACH list_comprehension_elements_or_expr
{
$$ = new ExpressionLcEach($2);
}
| TOK_FOR '(' arguments_call ')' list_comprehension_elements_or_expr
{
$$ = $5;
@ -458,19 +461,32 @@ list_comprehension_elements:
for (int i = $3->size()-1; i >= 0; i--) {
AssignmentList arglist;
arglist.push_back((*$3)[i]);
Expression *e = new ExpressionLc("for", arglist, $$);
Expression *e = new ExpressionLcFor(arglist, $$);
$$ = e;
}
delete $3;
}
| TOK_IF '(' expr ')' list_comprehension_elements_or_expr
{
$$ = new ExpressionLc("if", $3, $5);
$$ = new ExpressionLcIf($3, $5, 0);
}
| TOK_IF '(' expr ')' list_comprehension_elements_or_expr TOK_ELSE list_comprehension_elements_or_expr
{
$$ = new ExpressionLcIf($3, $5, $7);
}
;
// list_comprehension_elements with optional parenthesis
list_comprehension_elements_p:
list_comprehension_elements
| '(' list_comprehension_elements ')'
{
$$ = $2;
}
;
list_comprehension_elements_or_expr:
list_comprehension_elements
list_comprehension_elements_p
| expr
;
@ -484,7 +500,11 @@ vector_expr:
{
$$ = new ExpressionVector($1);
}
| vector_expr ',' optional_commas expr
| list_comprehension_elements
{
$$ = new ExpressionVector($1);
}
| vector_expr ',' optional_commas list_comprehension_elements_or_expr
{
$$ = $1;
$$->children.push_back($4);

View file

@ -6,7 +6,7 @@ ScadLexer::ScadLexer(QObject *parent) : QsciLexerCPP(parent)
{
// -> Style: Keyword (lexer.l)
keywordSet[0] =
"if else let for module function true false undef "
"if else let for each module function true false undef "
"include use";
// -> Style: KeywordSet2 (func.cc)

View file

@ -0,0 +1,8 @@
echo([ for (a = [0,1,2]) if (a == 1) "-" else "+" ]);
echo([ for (a = [0,1,2]) if (a > 0) if (a == 1) "A" else "B" ]);
echo([ for (a = [0,1,2]) if (a > 0) if (a == 1) "A" else "B" else "-" ]);
echo([ for (a = [0 : 3]) if (a < 2) if (a < 1) ["+", a] else ["-", a] ]);
echo([ for (a = [0 : 3]) if (a < 2) ( if (a < 1) ["+", a] ) else ["-", a] ]);
echo([ for (a = [2 : 4]) each [ a, a*a ] ]);
function f() = [ for (a = [0 : 4]) pow(2, a) ];
echo([ each ["a", "b"], each [-5 : -2 : -9], each f(), each "c", each 42, each true ]);

View file

@ -27,3 +27,5 @@ echo([ for (a=[0:1]) if (true) if (true) a]);
echo([ for (a=[0:1]) for (b=[a:2]) [b,a]]);
echo([ for (a=[0:1]) if (true) for (b=[a:2]) [b,a]]);
echo([ for (a=[0:1]) if (true) if (true) for (b=[a:2]) [b,a]]);
echo([ -1, for (a = [0:1:3]) a, for (b = [3:-1:0]) b, -1 ]);
echo([ for (a = [2:3]) a * 2, for (a = [5:9]) if ((a % 2) == 0) [ a, a + 1 ] , -1 ]);

View file

@ -30,3 +30,7 @@ aa = k ? l : m;
bb = n[o];
cc = let(a=1) a;
dd = [for (a=[0,1]) let(b=a) if (true) b];
ee = ["abc", for (a=[0,1]) let(b=a) if (true) b, true, for(c=[1:3]) c, 3];
ff = [for (a=[0,1]) if (a == 0) "A" else ( "B" )];
gg = [each [ "a", 0, false ]];
hh = [for (a = [0 : 3]) if (a < 2) ( if (a < 1) ["+", a] ) else ["-", a] ];

View file

@ -885,6 +885,17 @@ macro(disable_tests)
endforeach()
endmacro()
#
# Tags tests as experimental. This will add all the --enable=<feature>
# options for the tagged tests.
#
macro(experimental_tests)
foreach (TESTNAME ${ARGN})
# message("Marking as experimental ${TESTNAME}")
list(APPEND EXPERIMENTAL_TESTS ${TESTNAME})
endforeach()
endmacro()
#
# Tags the given tests as belonging to the given CONFIG, i.e. will
# only be executed when run using ctest -C <CONFIG>
@ -978,16 +989,12 @@ function(add_cmdline_test TESTCMD_BASENAME)
if (${DISABLED} EQUAL -1)
set(EXPERIMENTAL_OPTION "")
string(REGEX MATCH "csgtexttest" match_result1 ${TEST_FULLNAME})
string(REGEX MATCH "cgalstlsanity" match_result2 ${TEST_FULLNAME})
string(REGEX MATCH "dumptest" match_result3 ${TEST_FULLNAME})
if( "${match_result1}" STREQUAL "" )
if( "${match_result2}" STREQUAL "" )
if( "${match_result3}" STREQUAL "" )
set(EXPERIMENTAL_OPTION "")
endif()
endif()
list(FIND EXPERIMENTAL_TESTS ${TEST_FULLNAME} EXPERIMENTAL)
if (${EXPERIMENTAL} EQUAL -1)
set(EXPERIMENTAL_OPTION "")
else()
set(EXPERIMENTAL_OPTION "--enable=lc-each" "--enable=lc-else")
endif()
# 2D tests should be viewed from the top, not an angle.
@ -1166,8 +1173,7 @@ list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allmodules.scad)
list(APPEND CGALPNGTEST_2D_FILES ${FEATURES_2D_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_2D_FILES})
list(APPEND CGALPNGTEST_3D_FILES ${FEATURES_3D_FILES} ${DEPRECATED_3D_FILES} ${ISSUES_3D_FILES}
${EXAMPLE_3D_FILES})
list(APPEND CGALPNGTEST_3D_FILES ${FEATURES_3D_FILES} ${DEPRECATED_3D_FILES} ${ISSUES_3D_FILES} ${EXAMPLE_3D_FILES})
list(APPEND CGALPNGTEST_3D_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/include-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/use-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/transform-nan-inf-tests.scad
@ -1300,6 +1306,8 @@ disable_tests(csgpngtest_primitive-inf-tests
cgalpngtest_empty-shape-tests
csgpngtest_issue1258)
experimental_tests(echotest_list-comprehensions-experimental)
# Test config handling
# Heavy tests are tests taking more than 10 seconds on a development computer

View file

@ -24,3 +24,5 @@ ECHO: [0, 1]
ECHO: [[0, 0], [1, 0], [2, 0], [1, 1], [2, 1]]
ECHO: [[0, 0], [1, 0], [2, 0], [1, 1], [2, 1]]
ECHO: [[0, 0], [1, 0], [2, 0], [1, 1], [2, 1]]
ECHO: [-1, 0, 1, 2, 3, 3, 2, 1, 0, -1]
ECHO: [4, 6, [6, 7], [8, 9], -1]

View file

@ -0,0 +1,7 @@
ECHO: ["+", "-", "+"]
ECHO: ["A", "B"]
ECHO: ["-", "A", "B"]
ECHO: [["+", 0], ["-", 1]]
ECHO: [["+", 0], ["-", 2], ["-", 3]]
ECHO: [2, 4, 3, 9, 4, 16]
ECHO: ["a", "b", -5, -7, -9, 1, 2, 4, 8, 16, "c", 42, true]

View file

@ -29,5 +29,9 @@ z = j;
aa = (k ? l : m);
bb = n[o];
cc = let(a = 1) a;
dd = [for(a = [0, 1]) let(b = a) if(true) b];
dd = [for(a = [0, 1]) (let(b = a) (if(true) (b)))];
ee = ["abc", for(a = [0, 1]) (let(b = a) (if(true) (b))), true, for(c = [1 : 3]) (c), 3];
ff = [for(a = [0, 1]) (if((a == 0)) ("A") else ("B"))];
gg = [each (["a", 0, false])];
hh = [for(a = [0 : 3]) (if((a < 2)) (if((a < 1)) (["+", a])) else (["-", a]))];