<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/x509_openssl_util.h"

#include "base/logging.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "net/base/x509_cert_types.h"

namespace net {

namespace x509_openssl_util {

bool ParsePrincipalKeyAndValueByIndex(X509_NAME* name,
                                      int index,
                                      std::string* key,
                                      std::string* value) {
  X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, index);
  if (!entry)
    return false;

  if (key) {
    ASN1_OBJECT* object = X509_NAME_ENTRY_get_object(entry);
    key->assign(OBJ_nid2sn(OBJ_obj2nid(object)));
  }

  ASN1_STRING* data = X509_NAME_ENTRY_get_data(entry);
  if (!data)
    return false;

  unsigned char* buf = NULL;
  int len = ASN1_STRING_to_UTF8(&buf, data);
  if (len <= 0)
    return false;

  value->assign(reinterpret_cast<const char*>(buf), len);
  OPENSSL_free(buf);
  return true;
}

bool ParsePrincipalValueByIndex(X509_NAME* name,
                                int index,
                                std::string* value) {
  return ParsePrincipalKeyAndValueByIndex(name, index, NULL, value);
}

bool ParsePrincipalValueByNID(X509_NAME* name, int nid, std::string* value) {
  int index = X509_NAME_get_index_by_NID(name, nid, -1);
  if (index < 0)
    return false;

  return ParsePrincipalValueByIndex(name, index, value);
}

bool ParseDate(ASN1_TIME* x509_time, base::Time* time) {
  if (!x509_time ||
      (x509_time->type != V_ASN1_UTCTIME &&
       x509_time->type != V_ASN1_GENERALIZEDTIME))
    return false;

  base::StringPiece str_date(reinterpret_cast<const char*>(x509_time->data),
                             x509_time->length);

  CertDateFormat format = x509_time->type == V_ASN1_UTCTIME ?
      CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME;
  return ParseCertificateDate(str_date, format, time);
}

// TODO(joth): Investigate if we can upstream this into the OpenSSL library,
// to avoid duplicating this logic across projects.
bool VerifyHostname(const std::string& hostname,
                    const std::vector<std::string>& cert_names) {
  DCHECK(!hostname.empty());

  // Simple host name validation. A valid domain name must only contain
  // alpha, digits, hyphens, and dots. An IP address may have digits and dots,
  // and also square braces and colons for IPv6 addresses.
  std::string reference_name;
  reference_name.reserve(hostname.length());

  bool found_alpha = false;
  bool found_ip6_chars = false;
  bool found_hyphen = false;
  int dot_count = 0;

  size_t first_dot_index = std::string::npos;
  for (std::string::const_iterator it = hostname.begin();
       it != hostname.end(); ++it) {
    char c = *it;
    if (IsAsciiAlpha(c)) {
      found_alpha = true;
      c = base::ToLowerASCII(c);
    } else if (c == '.') {
      ++dot_count;
      if (first_dot_index == std::string::npos)
        first_dot_index = reference_name.length();
    } else if (c == ':') {
      found_ip6_chars = true;
    } else if (c == '-') {
      found_hyphen = true;
    } else if (!IsAsciiDigit(c)) {
      LOG(WARNING) << "Invalid char " << c << " in hostname " << hostname;
      return false;
    }
    reference_name.push_back(c);
  }
  DCHECK(!reference_name.empty());

  if (found_ip6_chars || !found_alpha) {
    // For now we just do simple localhost IP address support, primarily as
    // it's needed by the test server. TODO(joth): Replace this with full IP
    // address support. See http://crbug.com/62973
    if (hostname == "127.0.0.1" &&
        std::find(cert_names.begin(), cert_names.end(), hostname) !=
            cert_names.end()) {
      DVLOG(1) << "Allowing localhost IP certificate: " << hostname;
      return true;
    }
    NOTIMPLEMENTED() << hostname;  // See comment above.
    return false;
  }

  // |wildcard_domain| is the remainder of |host| after the leading host
  // component is stripped off, but includes the leading dot e.g.
  // "www.f.com" -> ".f.com".
  // If there is no meaningful domain part to |host| (e.g. it is an IP address
  // or contains no dots) then |wildcard_domain| will be empty.
  // We required at least 3 components (i.e. 2 dots) as a basic protection
  // against too-broad wild-carding.
  base::StringPiece wildcard_domain;
  if (found_alpha && !found_ip6_chars && dot_count >= 2) {
    DCHECK(first_dot_index != std::string::npos);
    wildcard_domain = reference_name;
    wildcard_domain.remove_prefix(first_dot_index);
    DCHECK(wildcard_domain.starts_with("."));
  }

  for (std::vector<std::string>::const_iterator it = cert_names.begin();
       it != cert_names.end(); ++it) {
    // Catch badly corrupt cert names up front.
    if (it->empty() || it->find('\0') != std::string::npos) {
      LOG(WARNING) << "Bad name in cert: " << *it;
      continue;
    }
    const std::string cert_name_string(StringToLowerASCII(*it));
    base::StringPiece cert_match(cert_name_string);

    // Remove trailing dot, if any.
    if (cert_match.ends_with("."))
      cert_match.remove_suffix(1);

    // The hostname must be at least as long as the cert name it is matching,
    // as we require the wildcard (if present) to match at least one character.
    if (cert_match.length() > reference_name.length())
      continue;

    if (cert_match == reference_name)
      return true;

    // Next see if this cert name starts with a wildcard, so long as the
    // hostname we're matching against has a valid 'domain' part to match.
    // Note the "-10" version of draft-saintandre-tls-server-id-check allows
    // the wildcard to appear anywhere in the leftmost label, rather than
    // requiring it to be the only character. See also http://crbug.com/60719
    if (wildcard_domain.empty() || !cert_match.starts_with("*"))
      continue;

    // Erase the * but not the . from the domain, as we need to include the dot
    // in the comparison.
    cert_match.remove_prefix(1);

    // Do character by character comparison on the remainder to see
    // if we have a wildcard match. This intentionally does no special handling
    // for any other wildcard characters in |domain|; alternatively it could
    // detect these and skip those candidate cert names.
    if (cert_match == wildcard_domain)
      return true;
  }
  DVLOG(1) << "Could not find any match for " << hostname
           << " (canonicalized as " << reference_name
           << ") in cert names " << JoinString(cert_names, '|');
  return false;
}

}  // namespace x509_openssl_util

}  // namespace net
<commit_msg>Fixes OpenSSL build: adds missing include file<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/x509_openssl_util.h"

#include <algorithm>

#include "base/logging.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "net/base/x509_cert_types.h"

namespace net {

namespace x509_openssl_util {

bool ParsePrincipalKeyAndValueByIndex(X509_NAME* name,
                                      int index,
                                      std::string* key,
                                      std::string* value) {
  X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, index);
  if (!entry)
    return false;

  if (key) {
    ASN1_OBJECT* object = X509_NAME_ENTRY_get_object(entry);
    key->assign(OBJ_nid2sn(OBJ_obj2nid(object)));
  }

  ASN1_STRING* data = X509_NAME_ENTRY_get_data(entry);
  if (!data)
    return false;

  unsigned char* buf = NULL;
  int len = ASN1_STRING_to_UTF8(&buf, data);
  if (len <= 0)
    return false;

  value->assign(reinterpret_cast<const char*>(buf), len);
  OPENSSL_free(buf);
  return true;
}

bool ParsePrincipalValueByIndex(X509_NAME* name,
                                int index,
                                std::string* value) {
  return ParsePrincipalKeyAndValueByIndex(name, index, NULL, value);
}

bool ParsePrincipalValueByNID(X509_NAME* name, int nid, std::string* value) {
  int index = X509_NAME_get_index_by_NID(name, nid, -1);
  if (index < 0)
    return false;

  return ParsePrincipalValueByIndex(name, index, value);
}

bool ParseDate(ASN1_TIME* x509_time, base::Time* time) {
  if (!x509_time ||
      (x509_time->type != V_ASN1_UTCTIME &&
       x509_time->type != V_ASN1_GENERALIZEDTIME))
    return false;

  base::StringPiece str_date(reinterpret_cast<const char*>(x509_time->data),
                             x509_time->length);

  CertDateFormat format = x509_time->type == V_ASN1_UTCTIME ?
      CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME;
  return ParseCertificateDate(str_date, format, time);
}

// TODO(joth): Investigate if we can upstream this into the OpenSSL library,
// to avoid duplicating this logic across projects.
bool VerifyHostname(const std::string& hostname,
                    const std::vector<std::string>& cert_names) {
  DCHECK(!hostname.empty());

  // Simple host name validation. A valid domain name must only contain
  // alpha, digits, hyphens, and dots. An IP address may have digits and dots,
  // and also square braces and colons for IPv6 addresses.
  std::string reference_name;
  reference_name.reserve(hostname.length());

  bool found_alpha = false;
  bool found_ip6_chars = false;
  bool found_hyphen = false;
  int dot_count = 0;

  size_t first_dot_index = std::string::npos;
  for (std::string::const_iterator it = hostname.begin();
       it != hostname.end(); ++it) {
    char c = *it;
    if (IsAsciiAlpha(c)) {
      found_alpha = true;
      c = base::ToLowerASCII(c);
    } else if (c == '.') {
      ++dot_count;
      if (first_dot_index == std::string::npos)
        first_dot_index = reference_name.length();
    } else if (c == ':') {
      found_ip6_chars = true;
    } else if (c == '-') {
      found_hyphen = true;
    } else if (!IsAsciiDigit(c)) {
      LOG(WARNING) << "Invalid char " << c << " in hostname " << hostname;
      return false;
    }
    reference_name.push_back(c);
  }
  DCHECK(!reference_name.empty());

  if (found_ip6_chars || !found_alpha) {
    // For now we just do simple localhost IP address support, primarily as
    // it's needed by the test server. TODO(joth): Replace this with full IP
    // address support. See http://crbug.com/62973
    if (hostname == "127.0.0.1" &&
        std::find(cert_names.begin(), cert_names.end(), hostname) !=
            cert_names.end()) {
      DVLOG(1) << "Allowing localhost IP certificate: " << hostname;
      return true;
    }
    NOTIMPLEMENTED() << hostname;  // See comment above.
    return false;
  }

  // |wildcard_domain| is the remainder of |host| after the leading host
  // component is stripped off, but includes the leading dot e.g.
  // "www.f.com" -> ".f.com".
  // If there is no meaningful domain part to |host| (e.g. it is an IP address
  // or contains no dots) then |wildcard_domain| will be empty.
  // We required at least 3 components (i.e. 2 dots) as a basic protection
  // against too-broad wild-carding.
  base::StringPiece wildcard_domain;
  if (found_alpha && !found_ip6_chars && dot_count >= 2) {
    DCHECK(first_dot_index != std::string::npos);
    wildcard_domain = reference_name;
    wildcard_domain.remove_prefix(first_dot_index);
    DCHECK(wildcard_domain.starts_with("."));
  }

  for (std::vector<std::string>::const_iterator it = cert_names.begin();
       it != cert_names.end(); ++it) {
    // Catch badly corrupt cert names up front.
    if (it->empty() || it->find('\0') != std::string::npos) {
      LOG(WARNING) << "Bad name in cert: " << *it;
      continue;
    }
    const std::string cert_name_string(StringToLowerASCII(*it));
    base::StringPiece cert_match(cert_name_string);

    // Remove trailing dot, if any.
    if (cert_match.ends_with("."))
      cert_match.remove_suffix(1);

    // The hostname must be at least as long as the cert name it is matching,
    // as we require the wildcard (if present) to match at least one character.
    if (cert_match.length() > reference_name.length())
      continue;

    if (cert_match == reference_name)
      return true;

    // Next see if this cert name starts with a wildcard, so long as the
    // hostname we're matching against has a valid 'domain' part to match.
    // Note the "-10" version of draft-saintandre-tls-server-id-check allows
    // the wildcard to appear anywhere in the leftmost label, rather than
    // requiring it to be the only character. See also http://crbug.com/60719
    if (wildcard_domain.empty() || !cert_match.starts_with("*"))
      continue;

    // Erase the * but not the . from the domain, as we need to include the dot
    // in the comparison.
    cert_match.remove_prefix(1);

    // Do character by character comparison on the remainder to see
    // if we have a wildcard match. This intentionally does no special handling
    // for any other wildcard characters in |domain|; alternatively it could
    // detect these and skip those candidate cert names.
    if (cert_match == wildcard_domain)
      return true;
  }
  DVLOG(1) << "Could not find any match for " << hostname
           << " (canonicalized as " << reference_name
           << ") in cert names " << JoinString(cert_names, '|');
  return false;
}

}  // namespace x509_openssl_util

}  // namespace net
<|endoftext|>


<commit_before><commit_msg>refine estimate for temporary verts buffer, update comments<commit_after><|endoftext|>


<commit_before>/*
  Copyright 2013 SINTEF ICT, Applied Mathematics.
*/

#include "Common.hpp"
#include "EquelleType.hpp"
#include "SymbolTable.hpp"
#include "ASTNodes.hpp"
#include "ParseActions.hpp"
#include <sstream>

// ------ Parsing event handlers ------

SequenceNode* handleProgram(SequenceNode* lineblocknode)
{
    SymbolTable::setProgram(lineblocknode);
    return lineblocknode;
}

Node* handleNumber(const double num)
{
    return new NumberNode(num);
}

Node* handleIdentifier(const std::string& name)
{
    if (SymbolTable::isVariableDeclared(name)) {
        return new VarNode(name);
    } else {
        if (SymbolTable::isFunctionDeclared(name)) {
            return new FuncRefNode(name);
        } else {
            // This is a small problem: we want the error below, to catch
            // usage of undeclared identifiers, but the function start section
            // would then generate errors, because we are not yet in the function
            // scope.

            // std::string e("unknown identifier ");
            // e += name;
            // yyerror(e.c_str());
            return new JustAnIdentifierNode(name);
        }
    }
}

VarDeclNode* handleDeclaration(const std::string& name, TypeNode* type)
{
    EquelleType t = type->type();
    SymbolTable::declareVariable(name, t);
    return new VarDeclNode(name, type);
}

VarAssignNode* handleAssignment(const std::string& name, Node* expr)
{
    // If already declared...
    if (SymbolTable::isVariableDeclared(name)) {
        // Check if already assigned.
        if (SymbolTable::isVariableAssigned(name) && !SymbolTable::variableType(name).isMutable()) {
            std::string err_msg = "variable already assigned, cannot re-assign ";
            err_msg += name;
            yyerror(err_msg.c_str());
            return nullptr;
        }
        // Check that declared type matches right hand side.
        EquelleType lhs_type = SymbolTable::variableType(name);
        EquelleType rhs_type = expr->type();
        if (lhs_type != rhs_type) {
            // Check for special case: variable declared to have type
            // 'Collection Of <Entity> Subset Of <Some entityset>',
            // actual setting its grid mapping is postponed until the entityset
            // has been created by a function call.
            // That means we have to set the actual grid mapping here.
            if (lhs_type.gridMapping() == PostponedDefinition
                && lhs_type.basicType() == rhs_type.basicType()
                && lhs_type.isCollection() && rhs_type.isCollection()
                && SymbolTable::isSubset(rhs_type.gridMapping(), lhs_type.subsetOf())) {
                // OK, should make postponed definition of the variable.
                SymbolTable::setVariableType(name, rhs_type);
                SymbolTable::setEntitySetName(rhs_type.gridMapping(), name);
            } else {
                std::string err_msg = "mismatch between type in assignment and declaration for ";
                err_msg += name;
                yyerror(err_msg.c_str());
                return nullptr;
            }
        }
    } else {
        // Setting the gridmapping, as in the block above. Only this is
        // the direct (no previous declaration) assignment scenario.
        const int gm = expr->type().gridMapping();
        if (gm != NotApplicable && SymbolTable::entitySetName(gm) == "AnonymousEntitySet") {
            SymbolTable::setEntitySetName(gm, name);
        }
        SymbolTable::declareVariable(name, expr->type());
    }

    // Set variable to assigned (unless mutable) and return.
    if (!SymbolTable::variableType(name).isMutable()) {
        SymbolTable::setVariableAssigned(name, true);
    }
    return new VarAssignNode(name, expr);
}

Node* handleFuncDeclaration(const std::string& name, FuncTypeNode* ftype)
{
    SymbolTable::renameCurrentFunction(name);
    SymbolTable::retypeCurrentFunction(ftype->funcType());
    SymbolTable::setCurrentFunction(SymbolTable::getCurrentFunction().parentScope());
    return new FuncDeclNode(name, ftype);
}

Node* handleFuncStart(const std::string& name, Node* funcargs)
{
    SymbolTable::setCurrentFunction(name);
    return new FuncStartNode(name, funcargs);
}

void handleFuncStartType()
{
    SymbolTable::declareFunction("TemporaryFunction");
    SymbolTable::setCurrentFunction("TemporaryFunction");
}

SequenceNode* handleBlock(SequenceNode* block)
{
    // This is called after the block AST has been constructed,
    // so we should switch back to Main scope.
    SymbolTable::setCurrentFunction(SymbolTable::getCurrentFunction().parentScope());
    return block;
}

FuncAssignNode* handleFuncAssignment(Node* funcstart, SequenceNode* fbody)
{
    return new FuncAssignNode(funcstart, fbody);
}

ReturnStatementNode* handleReturnStatement(Node* expr)
{
    return new ReturnStatementNode(expr);
}

Node* handleDeclarationAssign(const std::string& name, TypeNode* type, Node* expr)
{
    SequenceNode* seq = new SequenceNode;
    seq->pushNode(handleDeclaration(name, type));
    seq->pushNode(handleAssignment(name, expr));
    return seq;
}

TypeNode* handleCollection(TypeNode* btype, Node* gridmapping, Node* subsetof)
{
    assert(gridmapping == nullptr || subsetof == nullptr);
    EquelleType bt = btype->type();
    if (!bt.isBasic()) {
        std::string errmsg = "attempting to declare a Collection Of <nonsense>";
        yyerror(errmsg.c_str());
    }
    int gm = NotApplicable;
    if (gridmapping) {
        if (!gridmapping->type().isEntityCollection() || gridmapping->type().gridMapping() == NotApplicable) {
            yyerror("a Collection must be On a Collection of Cell, Face etc.");
        } else {
            gm = gridmapping->type().gridMapping();
        }
    }
    int subset = NotApplicable;
    if (subsetof) {
        // We are creating a new entity collection.
        if (!subsetof->type().isEntityCollection() || subsetof->type().gridMapping() == NotApplicable) {
            yyerror("a Collection must be Subset Of a Collection of Cell, Face etc.");
        } else {
            gm = PostponedDefinition;
            subset = subsetof->type().gridMapping();
        }
    }
    delete btype;
    delete gridmapping;
    delete subsetof;
    return new TypeNode(EquelleType(bt.basicType(), Collection, gm, subset));
}

FuncTypeNode* handleFuncType(FuncArgsDeclNode* argtypes, TypeNode* rtype)
{
    return new FuncTypeNode(FunctionType(argtypes->arguments(), rtype->type()));
}

FuncCallNode* handleFuncCall(const std::string& name, FuncArgsNode* args)
{
    const Function& f = SymbolTable::getFunction(name);
    // Check function call arguments.
    const auto argtypes = args->argumentTypes();
    if (argtypes.size() != f.functionType().arguments().size()) {
        std::string err_msg = "wrong number of arguments when calling function ";
        err_msg += name;
        yyerror(err_msg.c_str());
    }
    // At the moment, we do not check function argument types.
    // If the function returns a new entity set, we must declare it (even if anonymous).
    int dynsubret = f.functionType().dynamicSubsetReturn(args->argumentTypes());
    if (dynsubret != NotApplicable) {
        // Create a new entity collection.
        const int gm = SymbolTable::declareNewEntitySet("AnonymousEntitySet", dynsubret);
        return new FuncCallNode(name, args, gm);
    } else {
        return new FuncCallNode(name, args);
    }
}

FuncCallStatementNode* handleFuncCallStatement(FuncCallNode* fcall)
{
    return new FuncCallStatementNode(fcall);
}

BinaryOpNode* handleBinaryOp(BinaryOp op, Node* left, Node* right)
{
    EquelleType lt = left->type();
    EquelleType rt = right->type();
    if (!isNumericType(lt.basicType()) || !(isNumericType(rt.basicType()))) {
        yyerror("arithmetic binary operators only apply to numeric types");
    }
    if (lt.isCollection() && rt.isCollection()) {
        if (lt.gridMapping() != rt.gridMapping()) {
            yyerror("arithmetic binary operators on Collections only acceptable "
                    "if both sides are On the same set.");
        }
    }
    switch (op) {
    case Add:
        // Intentional fall-through.
    case Subtract:
        if (lt != rt) {
            yyerror("addition and subtraction only allowed between identical types.");
        }
        break;
    case Multiply:
        if (lt.basicType() == Vector && rt.basicType() == Vector) {
            yyerror("cannot multiply two 'Vector' types.");
        }
        break;
    case Divide:
        if (rt.basicType() != Scalar) {
            yyerror("can only divide by 'Scalar' types");
        }
        break;
    default:
        yyerror("internal compiler error in handleBinaryOp().");
    }
    return new BinaryOpNode(op, left, right);
}

ComparisonOpNode* handleComparison(ComparisonOp op, Node* left, Node* right)
{
    EquelleType lt = left->type();
    EquelleType rt = right->type();
    if ((lt.basicType() != Scalar) || (rt.basicType() != Scalar)) {
        yyerror("comparison operators can only be applied to scalars");
    }
    if (lt.isCollection() && rt.isCollection()) {
        if (lt.gridMapping() != rt.gridMapping()) {
            yyerror("comparison operators on Collections only acceptable "
                    "if both sides are On the same set.");
        }
    }
    return new ComparisonOpNode(op, left, right);
}

NormNode* handleNorm(Node* expr_to_norm)
{
    const BasicType bt = expr_to_norm->type().basicType();
    if (isEntityType(bt) || bt == Scalar || bt == Vector) {
        return new NormNode(expr_to_norm);
    } else {
        yyerror("can only take norm of Scalar, Vector, Cell, Face, Edge and Vertex types.");
        return new NormNode(expr_to_norm);
    }
}

UnaryNegationNode* handleUnaryNegation(Node* expr_to_negate)
{
    if (!isNumericType(expr_to_negate->type().basicType())) {
        yyerror("unary minus can only be applied to numeric types.");
    }
    return new UnaryNegationNode(expr_to_negate);
}

TrinaryIfNode* handleTrinaryIf(Node* predicate, Node* iftrue, Node* iffalse)
{
    const EquelleType pt = predicate->type();
    const EquelleType tt = iftrue->type();
    const EquelleType ft = iffalse->type();
    if (pt.basicType() != Bool) {
        yyerror("in trinary if '<predicate> ? <iftrue> : <iffalse>' "
                "<predicate> must be a Bool type.");
    }
    if (tt != ft) {
        yyerror("in trinary if '<predicate> ? <iftrue> : <iffalse>' "
                "<iftrue> and <iffalse> must have the same type.");
    }
    if ((pt.isCollection() != tt.isCollection()) ||
        (pt.gridMapping() != tt.gridMapping())) {
        yyerror("in trinary if '<predicate> ? <iftrue> : <iffalse>' "
                "all three expressions must be 'On' the same set.");
    }
    return new TrinaryIfNode(predicate, iftrue, iffalse);
}

OnNode* handleOn(Node* left, Node* right)
{
    if (!right->type().isEntityCollection()) {
        yyerror("in a '<left> On <right>' expression "
                "the expression <right> must be a Collection Of Cell, Face, Edge or Vertex.");
    }

    // Bug: this test is not quite true. <left> can be a Scalar for example.
    //
    // if (canonicalGridMappingEntity(left->type().gridMapping()) != right->type().basicType()) {
    //     yyerror("in a '<left> On <right>' expression "
    //             "the expression <right> must be a collection of the same kind of that which <left> is On.");
    // }

    // Bug: this test is not quite good, since we do not want to compare the two sets <left> and
    // <right> are On, but the one <left> is On with <right> itself. This is not really doable
    // currently.
    //
    // const int gml = left->type().gridMapping();
    // const int gmr = right->type().gridMapping();
    // if (left->type().isCollection()
    //     && !SymbolTable::isSubset(gml, gmr)
    //     && !SymbolTable::isSubset(gmr, gml)) {
    //     yyerror("in a '<left> On <right>' expression "
    //             "the entityset <right> must be a (non-strict) super- or sub-set of "
    //             "the set which <left> is 'On'.");
    // }
    return new OnNode(left, right);
}

StringNode* handleString(const std::string& content)
{
    return new StringNode(content);
}

TypeNode* handleMutableType(TypeNode* type_expr)
{
    EquelleType et = type_expr->type();
    et.setMutable(true);
    TypeNode* tn = new TypeNode(et);
    delete type_expr;
    return tn;
}

TypeNode* handleSequence(TypeNode* basic_type)
{
    const EquelleType et = basic_type->type();
    if (!et.isBasic()) {
        yyerror("cannot create a Sequence of non-basic types.");
    }
    return new TypeNode(EquelleType(et.basicType(), Sequence, et.gridMapping(), et.subsetOf()));
}

LoopNode* handleLoopStart(const std::string& loop_variable, const std::string& loop_set)
{
    // Check that loop_set is a sequence, extract its type.
    EquelleType loop_set_type;
    if (SymbolTable::isVariableDeclared(loop_set)) {
        loop_set_type = SymbolTable::variableType(loop_set);
        if (!loop_set_type.isSequence()) {
            std::string err_msg = "loop set must be a Sequence: ";
            err_msg += loop_set;
            yyerror(err_msg.c_str());
        }
    } else {
        std::string err_msg = "unknown variable used: ";
        err_msg += loop_set;
        yyerror(err_msg.c_str());
    }
    // Create LoopNode
    LoopNode* ln = new LoopNode(loop_variable, loop_set);
    // Create a name for the loop scope.
    static int next_loop_index = 0;
    std::ostringstream os;
    os << "ForLoopWithIndex" << next_loop_index++;
    // Set name in loop node, declare scope and
    // set to current.
    ln->setName(os.str());
    SymbolTable::declareFunction(os.str());
    SymbolTable::setCurrentFunction(os.str());
    // Declare loop variable
    SymbolTable::declareVariable(loop_variable, loop_set_type.basicType());
    return ln;
}

LoopNode* handleLoopStatement(LoopNode* loop_start, SequenceNode* loop_block)
{
    loop_start->setBlock(loop_block);
    return loop_start;
}

<commit_msg>Refined handleOn().<commit_after>/*
  Copyright 2013 SINTEF ICT, Applied Mathematics.
*/

#include "Common.hpp"
#include "EquelleType.hpp"
#include "SymbolTable.hpp"
#include "ASTNodes.hpp"
#include "ParseActions.hpp"
#include <sstream>

// ------ Parsing event handlers ------

SequenceNode* handleProgram(SequenceNode* lineblocknode)
{
    SymbolTable::setProgram(lineblocknode);
    return lineblocknode;
}

Node* handleNumber(const double num)
{
    return new NumberNode(num);
}

Node* handleIdentifier(const std::string& name)
{
    if (SymbolTable::isVariableDeclared(name)) {
        return new VarNode(name);
    } else {
        if (SymbolTable::isFunctionDeclared(name)) {
            return new FuncRefNode(name);
        } else {
            // This is a small problem: we want the error below, to catch
            // usage of undeclared identifiers, but the function start section
            // would then generate errors, because we are not yet in the function
            // scope.

            // std::string e("unknown identifier ");
            // e += name;
            // yyerror(e.c_str());
            return new JustAnIdentifierNode(name);
        }
    }
}

VarDeclNode* handleDeclaration(const std::string& name, TypeNode* type)
{
    EquelleType t = type->type();
    SymbolTable::declareVariable(name, t);
    return new VarDeclNode(name, type);
}

VarAssignNode* handleAssignment(const std::string& name, Node* expr)
{
    // If already declared...
    if (SymbolTable::isVariableDeclared(name)) {
        // Check if already assigned.
        if (SymbolTable::isVariableAssigned(name) && !SymbolTable::variableType(name).isMutable()) {
            std::string err_msg = "variable already assigned, cannot re-assign ";
            err_msg += name;
            yyerror(err_msg.c_str());
            return nullptr;
        }
        // Check that declared type matches right hand side.
        EquelleType lhs_type = SymbolTable::variableType(name);
        EquelleType rhs_type = expr->type();
        if (lhs_type != rhs_type) {
            // Check for special case: variable declared to have type
            // 'Collection Of <Entity> Subset Of <Some entityset>',
            // actual setting its grid mapping is postponed until the entityset
            // has been created by a function call.
            // That means we have to set the actual grid mapping here.
            if (lhs_type.gridMapping() == PostponedDefinition
                && lhs_type.basicType() == rhs_type.basicType()
                && lhs_type.isCollection() && rhs_type.isCollection()
                && SymbolTable::isSubset(rhs_type.gridMapping(), lhs_type.subsetOf())) {
                // OK, should make postponed definition of the variable.
                SymbolTable::setVariableType(name, rhs_type);
                SymbolTable::setEntitySetName(rhs_type.gridMapping(), name);
            } else {
                std::string err_msg = "mismatch between type in assignment and declaration for ";
                err_msg += name;
                yyerror(err_msg.c_str());
                return nullptr;
            }
        }
    } else {
        // Setting the gridmapping, as in the block above. Only this is
        // the direct (no previous declaration) assignment scenario.
        const int gm = expr->type().gridMapping();
        if (gm != NotApplicable && SymbolTable::entitySetName(gm) == "AnonymousEntitySet") {
            SymbolTable::setEntitySetName(gm, name);
        }
        SymbolTable::declareVariable(name, expr->type());
    }

    // Set variable to assigned (unless mutable) and return.
    if (!SymbolTable::variableType(name).isMutable()) {
        SymbolTable::setVariableAssigned(name, true);
    }
    return new VarAssignNode(name, expr);
}

Node* handleFuncDeclaration(const std::string& name, FuncTypeNode* ftype)
{
    SymbolTable::renameCurrentFunction(name);
    SymbolTable::retypeCurrentFunction(ftype->funcType());
    SymbolTable::setCurrentFunction(SymbolTable::getCurrentFunction().parentScope());
    return new FuncDeclNode(name, ftype);
}

Node* handleFuncStart(const std::string& name, Node* funcargs)
{
    SymbolTable::setCurrentFunction(name);
    return new FuncStartNode(name, funcargs);
}

void handleFuncStartType()
{
    SymbolTable::declareFunction("TemporaryFunction");
    SymbolTable::setCurrentFunction("TemporaryFunction");
}

SequenceNode* handleBlock(SequenceNode* block)
{
    // This is called after the block AST has been constructed,
    // so we should switch back to Main scope.
    SymbolTable::setCurrentFunction(SymbolTable::getCurrentFunction().parentScope());
    return block;
}

FuncAssignNode* handleFuncAssignment(Node* funcstart, SequenceNode* fbody)
{
    return new FuncAssignNode(funcstart, fbody);
}

ReturnStatementNode* handleReturnStatement(Node* expr)
{
    return new ReturnStatementNode(expr);
}

Node* handleDeclarationAssign(const std::string& name, TypeNode* type, Node* expr)
{
    SequenceNode* seq = new SequenceNode;
    seq->pushNode(handleDeclaration(name, type));
    seq->pushNode(handleAssignment(name, expr));
    return seq;
}

TypeNode* handleCollection(TypeNode* btype, Node* gridmapping, Node* subsetof)
{
    assert(gridmapping == nullptr || subsetof == nullptr);
    EquelleType bt = btype->type();
    if (!bt.isBasic()) {
        std::string errmsg = "attempting to declare a Collection Of <nonsense>";
        yyerror(errmsg.c_str());
    }
    int gm = NotApplicable;
    if (gridmapping) {
        if (!gridmapping->type().isEntityCollection() || gridmapping->type().gridMapping() == NotApplicable) {
            yyerror("a Collection must be On a Collection of Cell, Face etc.");
        } else {
            gm = gridmapping->type().gridMapping();
        }
    }
    int subset = NotApplicable;
    if (subsetof) {
        // We are creating a new entity collection.
        if (!subsetof->type().isEntityCollection() || subsetof->type().gridMapping() == NotApplicable) {
            yyerror("a Collection must be Subset Of a Collection of Cell, Face etc.");
        } else {
            gm = PostponedDefinition;
            subset = subsetof->type().gridMapping();
        }
    }
    delete btype;
    delete gridmapping;
    delete subsetof;
    return new TypeNode(EquelleType(bt.basicType(), Collection, gm, subset));
}

FuncTypeNode* handleFuncType(FuncArgsDeclNode* argtypes, TypeNode* rtype)
{
    return new FuncTypeNode(FunctionType(argtypes->arguments(), rtype->type()));
}

FuncCallNode* handleFuncCall(const std::string& name, FuncArgsNode* args)
{
    const Function& f = SymbolTable::getFunction(name);
    // Check function call arguments.
    const auto argtypes = args->argumentTypes();
    if (argtypes.size() != f.functionType().arguments().size()) {
        std::string err_msg = "wrong number of arguments when calling function ";
        err_msg += name;
        yyerror(err_msg.c_str());
    }
    // At the moment, we do not check function argument types.
    // If the function returns a new entity set, we must declare it (even if anonymous).
    int dynsubret = f.functionType().dynamicSubsetReturn(args->argumentTypes());
    if (dynsubret != NotApplicable) {
        // Create a new entity collection.
        const int gm = SymbolTable::declareNewEntitySet("AnonymousEntitySet", dynsubret);
        return new FuncCallNode(name, args, gm);
    } else {
        return new FuncCallNode(name, args);
    }
}

FuncCallStatementNode* handleFuncCallStatement(FuncCallNode* fcall)
{
    return new FuncCallStatementNode(fcall);
}

BinaryOpNode* handleBinaryOp(BinaryOp op, Node* left, Node* right)
{
    EquelleType lt = left->type();
    EquelleType rt = right->type();
    if (!isNumericType(lt.basicType()) || !(isNumericType(rt.basicType()))) {
        yyerror("arithmetic binary operators only apply to numeric types");
    }
    if (lt.isCollection() && rt.isCollection()) {
        if (lt.gridMapping() != rt.gridMapping()) {
            yyerror("arithmetic binary operators on Collections only acceptable "
                    "if both sides are On the same set.");
        }
    }
    switch (op) {
    case Add:
        // Intentional fall-through.
    case Subtract:
        if (lt != rt) {
            yyerror("addition and subtraction only allowed between identical types.");
        }
        break;
    case Multiply:
        if (lt.basicType() == Vector && rt.basicType() == Vector) {
            yyerror("cannot multiply two 'Vector' types.");
        }
        break;
    case Divide:
        if (rt.basicType() != Scalar) {
            yyerror("can only divide by 'Scalar' types");
        }
        break;
    default:
        yyerror("internal compiler error in handleBinaryOp().");
    }
    return new BinaryOpNode(op, left, right);
}

ComparisonOpNode* handleComparison(ComparisonOp op, Node* left, Node* right)
{
    EquelleType lt = left->type();
    EquelleType rt = right->type();
    if ((lt.basicType() != Scalar) || (rt.basicType() != Scalar)) {
        yyerror("comparison operators can only be applied to scalars");
    }
    if (lt.isCollection() && rt.isCollection()) {
        if (lt.gridMapping() != rt.gridMapping()) {
            yyerror("comparison operators on Collections only acceptable "
                    "if both sides are On the same set.");
        }
    }
    return new ComparisonOpNode(op, left, right);
}

NormNode* handleNorm(Node* expr_to_norm)
{
    const BasicType bt = expr_to_norm->type().basicType();
    if (isEntityType(bt) || bt == Scalar || bt == Vector) {
        return new NormNode(expr_to_norm);
    } else {
        yyerror("can only take norm of Scalar, Vector, Cell, Face, Edge and Vertex types.");
        return new NormNode(expr_to_norm);
    }
}

UnaryNegationNode* handleUnaryNegation(Node* expr_to_negate)
{
    if (!isNumericType(expr_to_negate->type().basicType())) {
        yyerror("unary minus can only be applied to numeric types.");
    }
    return new UnaryNegationNode(expr_to_negate);
}

TrinaryIfNode* handleTrinaryIf(Node* predicate, Node* iftrue, Node* iffalse)
{
    const EquelleType pt = predicate->type();
    const EquelleType tt = iftrue->type();
    const EquelleType ft = iffalse->type();
    if (pt.basicType() != Bool) {
        yyerror("in trinary if '<predicate> ? <iftrue> : <iffalse>' "
                "<predicate> must be a Bool type.");
    }
    if (tt != ft) {
        yyerror("in trinary if '<predicate> ? <iftrue> : <iffalse>' "
                "<iftrue> and <iffalse> must have the same type.");
    }
    if ((pt.isCollection() != tt.isCollection()) ||
        (pt.gridMapping() != tt.gridMapping())) {
        yyerror("in trinary if '<predicate> ? <iftrue> : <iffalse>' "
                "all three expressions must be 'On' the same set.");
    }
    return new TrinaryIfNode(predicate, iftrue, iffalse);
}

OnNode* handleOn(Node* left, Node* right)
{
    if (!right->type().isEntityCollection()) {
        yyerror("in a '<left> On <right>' expression "
                "the expression <right> must be a Collection Of Cell, Face, Edge or Vertex.");
    }
    if (left->type().isSequence()) {
        yyerror("cannot use On operator with a Sequence.");
    }

    if (left->type().isCollection()) {
        const int gml = left->type().gridMapping();
        if (SymbolTable::entitySetType(gml) != right->type().basicType()) {
            std::string err_msg;
            err_msg += "in a '<left> On <right>' expression the expression <right> must "
                "be a collection of the same kind of that which <left> is On. ";
            err_msg += "Collection on the left is On ";
            err_msg += SymbolTable::entitySetName(gml);
            yyerror(err_msg.c_str());
        }
        // Following test is wrong: cannot deal properly with rhs that is not an entity set.
        // const int gmr = right->type().gridMapping();
        // if (!SymbolTable::isSubset(gml, gmr)
        //     && !SymbolTable::isSubset(gmr, gml)) {
        //     yyerror("in a '<left> On <right>' expression "
        //             "the entityset <right> must be a (non-strict) super- or sub-set of "
        //             "the set which <left> is 'On'.");
        // }
    }
    return new OnNode(left, right);
}

StringNode* handleString(const std::string& content)
{
    return new StringNode(content);
}

TypeNode* handleMutableType(TypeNode* type_expr)
{
    EquelleType et = type_expr->type();
    et.setMutable(true);
    TypeNode* tn = new TypeNode(et);
    delete type_expr;
    return tn;
}

TypeNode* handleSequence(TypeNode* basic_type)
{
    const EquelleType et = basic_type->type();
    if (!et.isBasic()) {
        yyerror("cannot create a Sequence of non-basic types.");
    }
    return new TypeNode(EquelleType(et.basicType(), Sequence, et.gridMapping(), et.subsetOf()));
}

LoopNode* handleLoopStart(const std::string& loop_variable, const std::string& loop_set)
{
    // Check that loop_set is a sequence, extract its type.
    EquelleType loop_set_type;
    if (SymbolTable::isVariableDeclared(loop_set)) {
        loop_set_type = SymbolTable::variableType(loop_set);
        if (!loop_set_type.isSequence()) {
            std::string err_msg = "loop set must be a Sequence: ";
            err_msg += loop_set;
            yyerror(err_msg.c_str());
        }
    } else {
        std::string err_msg = "unknown variable used: ";
        err_msg += loop_set;
        yyerror(err_msg.c_str());
    }
    // Create LoopNode
    LoopNode* ln = new LoopNode(loop_variable, loop_set);
    // Create a name for the loop scope.
    static int next_loop_index = 0;
    std::ostringstream os;
    os << "ForLoopWithIndex" << next_loop_index++;
    // Set name in loop node, declare scope and
    // set to current.
    ln->setName(os.str());
    SymbolTable::declareFunction(os.str());
    SymbolTable::setCurrentFunction(os.str());
    // Declare loop variable
    SymbolTable::declareVariable(loop_variable, loop_set_type.basicType());
    return ln;
}

LoopNode* handleLoopStatement(LoopNode* loop_start, SequenceNode* loop_block)
{
    loop_start->setBlock(loop_block);
    return loop_start;
}

<|endoftext|>


<commit_before>#include <Core/Resources/Resources.hpp>

#include <algorithm>
#include <cpplocate/cpplocate.h>

namespace Ra {
namespace Core {
namespace Resources {
std::string getRadiumResourcesDir() {
    auto libraryPath =
        cpplocate::getLibraryPath( reinterpret_cast<void*>( getRadiumResourcesDir ) );
    std::replace(libraryPath.begin(), libraryPath.end(), '\\', '/');
    auto libraryDir  = libraryPath.substr( 0, libraryPath.find_last_of( '/' ) );
    auto baseDir     = libraryDir.substr( 0, libraryDir.find_last_of( '/' ) + 1 );

    return baseDir + "Resources/";
}

std::string getRadiumPluginsDir() {
    auto libraryPath = cpplocate::getLibraryPath( reinterpret_cast<void*>( getRadiumPluginsDir ) );
    std::replace(libraryPath.begin(), libraryPath.end(), '\\', '/');
    auto libraryDir  = libraryPath.substr( 0, libraryPath.find_last_of( '/' ) );
    auto baseDir     = libraryDir.substr( 0, libraryDir.find_last_of( '/' ) + 1 );

    return baseDir + "Plugins/lib/";
}

std::string getBaseDir() {
    return cpplocate::getModulePath();
}

ResourcesLocator::ResourcesLocator( void* symbol, const std::string& offset ) : m_basePath{""} {
    auto libraryPath = cpplocate::getLibraryPath( symbol );
    std::replace(libraryPath.begin(), libraryPath.end(), '\\', '/');
    auto libraryDir  = libraryPath.substr( 0, libraryPath.find_last_of( '/' ) );
    m_basePath       = libraryDir + offset;
}
const std::string& ResourcesLocator::getBasePath() {
    return m_basePath;
}
} // namespace Resources
} // namespace Core
} // namespace Ra
<commit_msg>Resource locator update to remove duplicated code.<commit_after>#include <Core/Resources/Resources.hpp>

#include <Core/Utils/Log.hpp>
#include <algorithm>
#include <cpplocate/cpplocate.h>

namespace Ra {
namespace Core {
namespace Resources {

std::string searchPath( std::string pattern, std::string offset, void* libSymbol ) {
    std::string baseDir = cpplocate::locatePath( pattern, offset, libSymbol );
    std::replace( baseDir.begin(), baseDir.end(), '\\', '/' );
    int nup = 0;
    while ( baseDir.rfind( "../", baseDir.size() - 1 - 3 * nup ) != std::string::npos )
    {
        ++nup;
    }
    baseDir = baseDir.substr( 0, baseDir.size() - 3 * nup - 1 );
    while ( nup-- )
    {
        baseDir = baseDir.substr( 0, baseDir.find_last_of( '/' ) + 1 );
    }
    return baseDir;
}

std::string getRadiumResourcesDir() {
    const std::string baseDir = searchPath(
        "Resources/Shaders", "../..", reinterpret_cast<void*>( getRadiumResourcesDir ) );
    return baseDir + "Resources/";
}

std::string getRadiumPluginsDir() {
    const std::string baseDir =
        searchPath( "Plugins/lib", "../../..", reinterpret_cast<void*>( getRadiumPluginsDir ) );
    return baseDir + "Plugins/lib/";
}

std::string getBaseDir() {
    return cpplocate::getModulePath();
}

ResourcesLocator::ResourcesLocator( void* symbol, const std::string& offset ) : m_basePath{""} {
    m_basePath = searchPath( "/", "", symbol ) + offset;
}
const std::string& ResourcesLocator::getBasePath() {
    return m_basePath;
}
} // namespace Resources
} // namespace Core
} // namespace Ra
<|endoftext|>


<commit_before>//
//  SerialPort.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 05/07/2016.
//  Copyright © 2016 Thomas Harte. All rights reserved.
//

#include "SerialBus.hpp"

#include <cstdio>

using namespace Commodore::Serial;

const char *::Commodore::Serial::StringForLine(Line line) {
	switch(line) {
		case ServiceRequest: return "Service request";
		case Attention: return "Attention";
		case Clock: return "Clock";
		case Data: return "Data";
		case Reset: return "Reset";
	}
}

void ::Commodore::Serial::AttachPortAndBus(std::shared_ptr<Port> port, std::shared_ptr<Bus> bus) {
	port->set_serial_bus(bus);
	bus->add_port(port);
}

void Bus::add_port(std::shared_ptr<Port> port) {
	ports_.push_back(port);
	for(int line = static_cast<int>(ServiceRequest); line <= static_cast<int>(Reset); line++) {
		// the addition of a new device may change the line output...
		set_line_output_did_change(static_cast<Line>(line));

		// ... but the new device will need to be told the current state regardless
		port->set_input(static_cast<Line>(line), line_levels_[line]);
	}
}

void Bus::set_line_output_did_change(Line line) {
	// i.e. I believe these lines to be open collector
	LineLevel new_line_level = High;
	for(std::weak_ptr<Port> port : ports_) {
		std::shared_ptr<Port> locked_port = port.lock();
		if(locked_port) {
			new_line_level = (LineLevel)((bool)new_line_level & (bool)locked_port->get_output(line));
		}
	}

//	printf("[Bus] %s is %s\n", StringForLine(line), new_line_level ? "high" : "low");

	// post an update only if one occurred
	if(new_line_level != line_levels_[line]) {
		line_levels_[line] = new_line_level;

		for(std::weak_ptr<Port> port : ports_) {
			std::shared_ptr<Port> locked_port = port.lock();
			if(locked_port) {
				locked_port->set_input(line, new_line_level);
			}
		}
	}
}

// MARK: - The debug port

void DebugPort::set_input(Line line, LineLevel value) {
	input_levels_[line] = value;

	printf("[Bus] %s is %s\n", StringForLine(line), value ? "high" : "low");
	if(!incoming_count_) {
		incoming_count_ = (!input_levels_[Line::Clock] && !input_levels_[Line::Data]) ? 8 : 0;
	} else {
		if(line == Line::Clock && value) {
			incoming_byte_ = (incoming_byte_ >> 1) | (input_levels_[Line::Data] ? 0x80 : 0x00);
		}
		incoming_count_--;
		if(incoming_count_ == 0) printf("[Bus] Observed %02x\n", incoming_byte_);
	}
}
<commit_msg>Makes it obvious to GCC that a return result is always supplied.<commit_after>//
//  SerialPort.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 05/07/2016.
//  Copyright © 2016 Thomas Harte. All rights reserved.
//

#include "SerialBus.hpp"

#include <cstdio>

using namespace Commodore::Serial;

const char *::Commodore::Serial::StringForLine(Line line) {
	switch(line) {
		case ServiceRequest: return "Service request";
		case Attention: return "Attention";
		case Clock: return "Clock";
		case Data: return "Data";
		case Reset: return "Reset";
		default: return nullptr;
	}
}

void ::Commodore::Serial::AttachPortAndBus(std::shared_ptr<Port> port, std::shared_ptr<Bus> bus) {
	port->set_serial_bus(bus);
	bus->add_port(port);
}

void Bus::add_port(std::shared_ptr<Port> port) {
	ports_.push_back(port);
	for(int line = static_cast<int>(ServiceRequest); line <= static_cast<int>(Reset); line++) {
		// the addition of a new device may change the line output...
		set_line_output_did_change(static_cast<Line>(line));

		// ... but the new device will need to be told the current state regardless
		port->set_input(static_cast<Line>(line), line_levels_[line]);
	}
}

void Bus::set_line_output_did_change(Line line) {
	// i.e. I believe these lines to be open collector
	LineLevel new_line_level = High;
	for(std::weak_ptr<Port> port : ports_) {
		std::shared_ptr<Port> locked_port = port.lock();
		if(locked_port) {
			new_line_level = (LineLevel)((bool)new_line_level & (bool)locked_port->get_output(line));
		}
	}

//	printf("[Bus] %s is %s\n", StringForLine(line), new_line_level ? "high" : "low");

	// post an update only if one occurred
	if(new_line_level != line_levels_[line]) {
		line_levels_[line] = new_line_level;

		for(std::weak_ptr<Port> port : ports_) {
			std::shared_ptr<Port> locked_port = port.lock();
			if(locked_port) {
				locked_port->set_input(line, new_line_level);
			}
		}
	}
}

// MARK: - The debug port

void DebugPort::set_input(Line line, LineLevel value) {
	input_levels_[line] = value;

	printf("[Bus] %s is %s\n", StringForLine(line), value ? "high" : "low");
	if(!incoming_count_) {
		incoming_count_ = (!input_levels_[Line::Clock] && !input_levels_[Line::Data]) ? 8 : 0;
	} else {
		if(line == Line::Clock && value) {
			incoming_byte_ = (incoming_byte_ >> 1) | (input_levels_[Line::Data] ? 0x80 : 0x00);
		}
		incoming_count_--;
		if(incoming_count_ == 0) printf("[Bus] Observed %02x\n", incoming_byte_);
	}
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2019, Wolfgang Merkt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//  * Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of  nor the names of its contributors may be used to
//    endorse or promote products derived from this software without specific
//    prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//

#include <exotica_double_integrator_dynamics_solver/double_integrator_dynamics_solver.h>

REGISTER_DYNAMICS_SOLVER_TYPE("DoubleIntegratorDynamicsSolver", exotica::DoubleIntegratorDynamicsSolver)

namespace exotica
{
void DoubleIntegratorDynamicsSolver::AssignScene(ScenePtr scene_in)
{
    const int num_positions_in = scene_in->GetKinematicTree().GetNumControlledJoints();

    num_positions_ = num_positions_in;
    num_velocities_ = num_positions_in;
    num_controls_ = num_positions_in;

    // Cf. https://en.wikipedia.org/wiki/Double_integrator
    A_ = Eigen::MatrixXd::Zero(num_positions_ + num_velocities_, num_positions_ + num_velocities_);
    A_.topRightCorner(num_velocities_, num_velocities_) = Eigen::MatrixXd::Identity(num_velocities_, num_velocities_);

    B_ = Eigen::MatrixXd::Zero(num_positions_ + num_velocities_, num_controls_);
    B_.bottomRightCorner(num_controls_, num_controls_) = Eigen::MatrixXd::Identity(num_controls_, num_controls_);

    fx_ = A_;
    fu_ = B_;
}

Eigen::VectorXd DoubleIntegratorDynamicsSolver::f(const StateVector& x, const ControlVector& u)
{
    return A_ * x + B_ * u;
}

void DoubleIntegratorDynamicsSolver::ComputeDerivatives(const StateVector& x, const ControlVector& u)
{
    // Nothing to do here.
}

Eigen::MatrixXd DoubleIntegratorDynamicsSolver::fx(const StateVector& x, const ControlVector& u)
{
    return A_;
}

Eigen::MatrixXd DoubleIntegratorDynamicsSolver::fu(const StateVector& x, const ControlVector& u)
{
    return B_;
}

}  // namespace exotica
<commit_msg>[exotica_double_integrator_dynamics_solver] Debug<commit_after>//
// Copyright (c) 2019, Wolfgang Merkt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//  * Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of  nor the names of its contributors may be used to
//    endorse or promote products derived from this software without specific
//    prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//

#include <exotica_double_integrator_dynamics_solver/double_integrator_dynamics_solver.h>

REGISTER_DYNAMICS_SOLVER_TYPE("DoubleIntegratorDynamicsSolver", exotica::DoubleIntegratorDynamicsSolver)

namespace exotica
{
void DoubleIntegratorDynamicsSolver::AssignScene(ScenePtr scene_in)
{
    const int num_positions_in = scene_in->GetKinematicTree().GetNumControlledJoints();
    if (debug_) HIGHLIGHT_NAMED("DoubleIntegratorDynamicsSolver::AssignScene", "Dimension: " << num_positions_in);

    num_positions_ = num_positions_in;
    num_velocities_ = num_positions_in;
    num_controls_ = num_positions_in;

    // Cf. https://en.wikipedia.org/wiki/Double_integrator
    A_ = Eigen::MatrixXd::Zero(num_positions_ + num_velocities_, num_positions_ + num_velocities_);
    A_.topRightCorner(num_velocities_, num_velocities_) = Eigen::MatrixXd::Identity(num_velocities_, num_velocities_);

    B_ = Eigen::MatrixXd::Zero(num_positions_ + num_velocities_, num_controls_);
    B_.bottomRightCorner(num_controls_, num_controls_) = Eigen::MatrixXd::Identity(num_controls_, num_controls_);

    fx_ = A_;
    fu_ = B_;
}

Eigen::VectorXd DoubleIntegratorDynamicsSolver::f(const StateVector& x, const ControlVector& u)
{
    return A_ * x + B_ * u;
}

void DoubleIntegratorDynamicsSolver::ComputeDerivatives(const StateVector& x, const ControlVector& u)
{
    // Nothing to do here.
}

Eigen::MatrixXd DoubleIntegratorDynamicsSolver::fx(const StateVector& x, const ControlVector& u)
{
    return A_;
}

Eigen::MatrixXd DoubleIntegratorDynamicsSolver::fu(const StateVector& x, const ControlVector& u)
{
    return B_;
}

}  // namespace exotica
<|endoftext|>


<commit_before>// MathPresso - Mathematical Expression Parser and JIT Compiler.

// Copyright (c) 2008-2010, Petr Kobalicek <kobalicek.petr@gmail.com>
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

// [Dependencies]
#include "MathPresso.h"
#include "MathPresso_AST_p.h"
#include "MathPresso_Parser_p.h"
#include "MathPresso_Tokenizer_p.h"
#include "MathPresso_Util_p.h"

namespace MathPresso {

// ============================================================================
// [Constants]
// ============================================================================

//! @internal
//!
//! @brief Operator priority.
static const int mpOperatorPriority[] =
{
  0 , // MOPERATOR_NONE
  5 , // MOPERATOR_ASSIGN
  10, // MOPERATOR_PLUS
  10, // MOPERATOR_MINUS
  15, // MOPERATOR_MUL
  15, // MOPERATOR_DIV
  15  // MOPERATOR_MOD
};

ExpressionParser::ExpressionParser(WorkContext& ctx, const char* input, size_t length) :
  _ctx(ctx),
  _tokenizer(input, length)
{
}

ExpressionParser::~ExpressionParser()
{
}

mresult_t ExpressionParser::parse(ASTElement** dst)
{
  return parseTree(dst);
}

mresult_t ExpressionParser::parseTree(ASTElement** dst)
{
  Vector<ASTElement*> elements;
  mresult_t result = MRESULT_OK;

  for (;;)
  {
    ASTElement* ast = NULL;
    if ((result = parseExpression(&ast, NULL, 0, false)) != MRESULT_OK)
      goto failed;
    if (ast) elements.append(ast);

    MP_ASSERT(_last.tokenType != MTOKEN_ERROR);
    switch (_last.tokenType)
    {
      case MTOKEN_END_OF_INPUT:
        goto finished;
      case MTOKEN_COMMA:
      case MTOKEN_RPAREN:
        result = MRESULT_UNEXPECTED_TOKEN;
        goto failed;
      case MTOKEN_SEMICOLON:
        // Skip semicolon and parse another expression.
        _tokenizer.next(&_last);
        break;
    }
  }

finished:
  if (elements.getLength() == 0)
  {
    return MRESULT_NO_EXPRESSION;
  }
  else if (elements.getLength() == 1)
  {
    *dst = elements[0];
    return MRESULT_OK;
  }
  else
  {
    ASTBlock* block = new ASTBlock(_ctx.genId());
    block->getChildrenVector().swap(elements);

    *dst = block;
    return MRESULT_OK;
  }

failed:
  for (size_t i = 0; i < elements.getLength(); i++) delete elements[i];
  return result;
}

mresult_t ExpressionParser::parseExpression(ASTElement** dst,
  ASTElement* _left,
  int minPriority,
  bool isInsideExpression)
{
  ASTElement* left = _left;
  ASTElement* right = NULL;

  mresult_t result = MRESULT_OK;
  uint op = MOPERATOR_NONE;
  uint om = MOPERATOR_NONE;

  Token& token = _last;
  Token helper;

  for (;;)
  {
    _tokenizer.next(&token);

    switch (token.tokenType)
    {
      // ----------------------------------------------------------------------
      case MTOKEN_ERROR:
        _tokenizer.back(&token);
        result = MRESULT_INVALID_TOKEN;
        goto failure;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_COMMA:
      case MTOKEN_SEMICOLON:
        if (left == NULL)
        {
          return (isInsideExpression)
            ? MRESULT_UNEXPECTED_TOKEN
            : MRESULT_OK;
        }
        // ... fall through ...
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_END_OF_INPUT:
        if (op != MOPERATOR_NONE)
        {
          // Expecting expression.
          result = MRESULT_EXPECTED_EXPRESSION;
          goto failure;
        }

        _tokenizer.back(&token);
        *dst = left;
        return MRESULT_OK;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_INTEGER:
      case MTOKEN_FLOAT:
        right = new ASTConstant(_ctx.genId(),
          om == MOPERATOR_MINUS ? -token.f : token.f);
        om = MOPERATOR_NONE;
        break;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_LPAREN:
        result = parseExpression(&right, NULL, 0, true);
        if (result != MRESULT_OK) goto failure;

        _tokenizer.next(&token);
        if (token.tokenType != MTOKEN_RPAREN)
        {
          result = MRESULT_UNEXPECTED_TOKEN;
          goto failure;
        }

        if (om == MOPERATOR_MINUS)
        {
          ASTTransform* transform = new ASTTransform(_ctx.genId());
          transform->setTransformType(MTRANSFORM_NEGATE);
          transform->setChild(right);
          right = transform;
        }

        om = MOPERATOR_NONE;
        break;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_RPAREN:
        if (op != MOPERATOR_NONE ||
            om != MOPERATOR_NONE)
        {
          result = MRESULT_UNEXPECTED_TOKEN;
          goto failure;
        }

        if (left == NULL && isInsideExpression)
        {
          result = MRESULT_UNEXPECTED_TOKEN;
          goto failure;
        }

        _tokenizer.back(&token);
        *dst = left;
        return MRESULT_OK;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_OPERATOR:
        if (token.operatorType == MOPERATOR_ASSIGN)
        {
          // Assignment inside expression is not allowed.
          if (isInsideExpression)
          {
            result = MRESULT_ASSIGNMENT_INSIDE_EXPRESSION;
            goto failure;
          }
          // Must be assigned to an variable.
          if (left == NULL || left->getElementType() != MELEMENT_VARIABLE)
          {
            result = MRESULT_ASSIGNMENT_TO_NON_VARIABLE;
            goto failure;
          }
        }

        if (op == MOPERATOR_NONE && left == NULL)
        {
          om = token.operatorType;
          if (om == MOPERATOR_PLUS || om == MOPERATOR_MINUS) continue;

          result = MRESULT_UNEXPECTED_TOKEN;
          goto failure;
        }

        op = token.operatorType;
        if (mpOperatorPriority[op] < minPriority)
        {
          _tokenizer.back(&token);

          *dst = left;
          return MRESULT_OK;
        }
        continue;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_SYMBOL:
      {
        const char* symbolName = _tokenizer.beg + token.pos;
        size_t symbolLength = token.len;

        Token ttoken;
        bool isFunction = (_tokenizer.peek(&ttoken) == MTOKEN_LPAREN);

        // Parse function.
        if (isFunction)
        {
          Function* function = _ctx._ctx->functions.get(symbolName, symbolLength);
          if (function == NULL)
          {
            result = MRESULT_NO_SYMBOL;
            goto failure;
          }

          // Parse LPAREN token again.
          _tokenizer.next(&ttoken);

          Vector<ASTElement*> arguments;
          bool first = true;

          // Parse function arguments.
          for (;;)
          {
            _tokenizer.next(&ttoken);

            if (ttoken.tokenType == MTOKEN_ERROR)
            {
              mpDeleteAll(arguments);
              result = MRESULT_INVALID_TOKEN;
              goto failure;
            }

            // ')' - End of function call.
            if (ttoken.tokenType == MTOKEN_RPAREN)
            {
              break;
            }

            // ',' - Arguments delimiter for non-first argument.
            if (!first)
            {
              if (ttoken.tokenType != MTOKEN_COMMA)
              {
                mpDeleteAll(arguments);
                result = MRESULT_EXPECTED_EXPRESSION;
                goto failure;
              }
            }
            else
            {
              _tokenizer.back(&ttoken);
            }

            // Expression.
            ASTElement* arg;
            if ((result = parseExpression(&arg, NULL, 0, true)) != MRESULT_OK)
            {
              mpDeleteAll(arguments);
              goto failure;
            }

            arguments.append(arg);
            first = false;
          }

          // Validate function arguments.
          if (function->getArguments() != arguments.getLength())
          {
            mpDeleteAll(arguments);
            result = MRESULT_ARGUMENTS_MISMATCH;
            goto failure;
          }

          // Done.
          ASTCall* call = new ASTCall(_ctx.genId(), function);
          call->swapArguments(arguments);
          right = call;
        }
        // Parse variable.
        else
        {
          Variable* var = _ctx._ctx->variables.get(symbolName, symbolLength);
          if (var == NULL)
          {
            result = MRESULT_NO_SYMBOL;
            goto failure;
          }

          if (var->type == MVARIABLE_CONSTANT)
            right = new ASTConstant(_ctx.genId(), var->c.value);
          else
            right = new ASTVariable(_ctx.genId(), var);
        }

        if (om == MOPERATOR_MINUS)
        {
          ASTTransform* transform = new ASTTransform(_ctx.genId());
          transform->setTransformType(MTRANSFORM_NEGATE);
          transform->setChild(right);
          right = transform;
        }

        om = MOPERATOR_NONE;
        break;
      }
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      default:
        MP_ASSERT_NOT_REACHED();
      // ----------------------------------------------------------------------
    }

    if (left)
    {
      _tokenizer.peek(&helper);

      if (helper.tokenType == MTOKEN_OPERATOR && mpOperatorPriority[op] < mpOperatorPriority[helper.operatorType])
      {
        result = parseExpression(&right, right, mpOperatorPriority[helper.operatorType], true);
        if (result != MRESULT_OK) { right = NULL; goto failure; }
      }

      ASTOperator* parent = new ASTOperator(_ctx.genId(), op);
      parent->setLeft(left);
      parent->setRight(right);

      left = parent;
      right = NULL;
      op = MOPERATOR_NONE;
    }
    else
    {
      left = right;
      right = NULL;
    }
  }

failure:
  if (left) delete left;
  if (right) delete right;

  *dst = NULL;
  return result;
}

} // MathPresso namespace
<commit_msg>fixed bug on ^ (power) operator priority<commit_after>// MathPresso - Mathematical Expression Parser and JIT Compiler.

// Copyright (c) 2008-2010, Petr Kobalicek <kobalicek.petr@gmail.com>
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

// [Dependencies]
#include "MathPresso.h"
#include "MathPresso_AST_p.h"
#include "MathPresso_Parser_p.h"
#include "MathPresso_Tokenizer_p.h"
#include "MathPresso_Util_p.h"

namespace MathPresso {

// ============================================================================
// [Constants]
// ============================================================================

//! @internal
//!
//! @brief Operator priority.
static const int mpOperatorPriority[] =
{
  0 , // MOPERATOR_NONE
  5 , // MOPERATOR_ASSIGN
  10, // MOPERATOR_PLUS
  10, // MOPERATOR_MINUS
  15, // MOPERATOR_MUL
  15, // MOPERATOR_DIV
  15, // MOPERATOR_MOD
  20  // MOPERATOR_POW
};

ExpressionParser::ExpressionParser(WorkContext& ctx, const char* input, size_t length) :
  _ctx(ctx),
  _tokenizer(input, length)
{
}

ExpressionParser::~ExpressionParser()
{
}

mresult_t ExpressionParser::parse(ASTElement** dst)
{
  return parseTree(dst);
}

mresult_t ExpressionParser::parseTree(ASTElement** dst)
{
  Vector<ASTElement*> elements;
  mresult_t result = MRESULT_OK;

  for (;;)
  {
    ASTElement* ast = NULL;
    if ((result = parseExpression(&ast, NULL, 0, false)) != MRESULT_OK)
      goto failed;
    if (ast) elements.append(ast);

    MP_ASSERT(_last.tokenType != MTOKEN_ERROR);
    switch (_last.tokenType)
    {
      case MTOKEN_END_OF_INPUT:
        goto finished;
      case MTOKEN_COMMA:
      case MTOKEN_RPAREN:
        result = MRESULT_UNEXPECTED_TOKEN;
        goto failed;
      case MTOKEN_SEMICOLON:
        // Skip semicolon and parse another expression.
        _tokenizer.next(&_last);
        break;
    }
  }

finished:
  if (elements.getLength() == 0)
  {
    return MRESULT_NO_EXPRESSION;
  }
  else if (elements.getLength() == 1)
  {
    *dst = elements[0];
    return MRESULT_OK;
  }
  else
  {
    ASTBlock* block = new ASTBlock(_ctx.genId());
    block->getChildrenVector().swap(elements);

    *dst = block;
    return MRESULT_OK;
  }

failed:
  for (size_t i = 0; i < elements.getLength(); i++) delete elements[i];
  return result;
}

mresult_t ExpressionParser::parseExpression(ASTElement** dst,
  ASTElement* _left,
  int minPriority,
  bool isInsideExpression)
{
  ASTElement* left = _left;
  ASTElement* right = NULL;

  mresult_t result = MRESULT_OK;
  uint op = MOPERATOR_NONE;
  uint om = MOPERATOR_NONE;

  Token& token = _last;
  Token helper;

  for (;;)
  {
    _tokenizer.next(&token);

    switch (token.tokenType)
    {
      // ----------------------------------------------------------------------
      case MTOKEN_ERROR:
        _tokenizer.back(&token);
        result = MRESULT_INVALID_TOKEN;
        goto failure;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_COMMA:
      case MTOKEN_SEMICOLON:
        if (left == NULL)
        {
          return (isInsideExpression)
            ? MRESULT_UNEXPECTED_TOKEN
            : MRESULT_OK;
        }
        // ... fall through ...
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_END_OF_INPUT:
        if (op != MOPERATOR_NONE)
        {
          // Expecting expression.
          result = MRESULT_EXPECTED_EXPRESSION;
          goto failure;
        }

        _tokenizer.back(&token);
        *dst = left;
        return MRESULT_OK;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_INTEGER:
      case MTOKEN_FLOAT:
        right = new ASTConstant(_ctx.genId(),
          om == MOPERATOR_MINUS ? -token.f : token.f);
        om = MOPERATOR_NONE;
        break;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_LPAREN:
        result = parseExpression(&right, NULL, 0, true);
        if (result != MRESULT_OK) goto failure;

        _tokenizer.next(&token);
        if (token.tokenType != MTOKEN_RPAREN)
        {
          result = MRESULT_UNEXPECTED_TOKEN;
          goto failure;
        }

        if (om == MOPERATOR_MINUS)
        {
          ASTTransform* transform = new ASTTransform(_ctx.genId());
          transform->setTransformType(MTRANSFORM_NEGATE);
          transform->setChild(right);
          right = transform;
        }

        om = MOPERATOR_NONE;
        break;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_RPAREN:
        if (op != MOPERATOR_NONE ||
            om != MOPERATOR_NONE)
        {
          result = MRESULT_UNEXPECTED_TOKEN;
          goto failure;
        }

        if (left == NULL && isInsideExpression)
        {
          result = MRESULT_UNEXPECTED_TOKEN;
          goto failure;
        }

        _tokenizer.back(&token);
        *dst = left;
        return MRESULT_OK;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_OPERATOR:
        if (token.operatorType == MOPERATOR_ASSIGN)
        {
          // Assignment inside expression is not allowed.
          if (isInsideExpression)
          {
            result = MRESULT_ASSIGNMENT_INSIDE_EXPRESSION;
            goto failure;
          }
          // Must be assigned to an variable.
          if (left == NULL || left->getElementType() != MELEMENT_VARIABLE)
          {
            result = MRESULT_ASSIGNMENT_TO_NON_VARIABLE;
            goto failure;
          }
        }

        if (op == MOPERATOR_NONE && left == NULL)
        {
          om = token.operatorType;
          if (om == MOPERATOR_PLUS || om == MOPERATOR_MINUS) continue;

          result = MRESULT_UNEXPECTED_TOKEN;
          goto failure;
        }

        op = token.operatorType;
        if (mpOperatorPriority[op] < minPriority)
        {
          _tokenizer.back(&token);

          *dst = left;
          return MRESULT_OK;
        }
        continue;
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      case MTOKEN_SYMBOL:
      {
        const char* symbolName = _tokenizer.beg + token.pos;
        size_t symbolLength = token.len;

        Token ttoken;
        bool isFunction = (_tokenizer.peek(&ttoken) == MTOKEN_LPAREN);

        // Parse function.
        if (isFunction)
        {
          Function* function = _ctx._ctx->functions.get(symbolName, symbolLength);
          if (function == NULL)
          {
            result = MRESULT_NO_SYMBOL;
            goto failure;
          }

          // Parse LPAREN token again.
          _tokenizer.next(&ttoken);

          Vector<ASTElement*> arguments;
          bool first = true;

          // Parse function arguments.
          for (;;)
          {
            _tokenizer.next(&ttoken);

            if (ttoken.tokenType == MTOKEN_ERROR)
            {
              mpDeleteAll(arguments);
              result = MRESULT_INVALID_TOKEN;
              goto failure;
            }

            // ')' - End of function call.
            if (ttoken.tokenType == MTOKEN_RPAREN)
            {
              break;
            }

            // ',' - Arguments delimiter for non-first argument.
            if (!first)
            {
              if (ttoken.tokenType != MTOKEN_COMMA)
              {
                mpDeleteAll(arguments);
                result = MRESULT_EXPECTED_EXPRESSION;
                goto failure;
              }
            }
            else
            {
              _tokenizer.back(&ttoken);
            }

            // Expression.
            ASTElement* arg;
            if ((result = parseExpression(&arg, NULL, 0, true)) != MRESULT_OK)
            {
              mpDeleteAll(arguments);
              goto failure;
            }

            arguments.append(arg);
            first = false;
          }

          // Validate function arguments.
          if (function->getArguments() != arguments.getLength())
          {
            mpDeleteAll(arguments);
            result = MRESULT_ARGUMENTS_MISMATCH;
            goto failure;
          }

          // Done.
          ASTCall* call = new ASTCall(_ctx.genId(), function);
          call->swapArguments(arguments);
          right = call;
        }
        // Parse variable.
        else
        {
          Variable* var = _ctx._ctx->variables.get(symbolName, symbolLength);
          if (var == NULL)
          {
            result = MRESULT_NO_SYMBOL;
            goto failure;
          }

          if (var->type == MVARIABLE_CONSTANT)
            right = new ASTConstant(_ctx.genId(), var->c.value);
          else
            right = new ASTVariable(_ctx.genId(), var);
        }

        if (om == MOPERATOR_MINUS)
        {
          ASTTransform* transform = new ASTTransform(_ctx.genId());
          transform->setTransformType(MTRANSFORM_NEGATE);
          transform->setChild(right);
          right = transform;
        }

        om = MOPERATOR_NONE;
        break;
      }
      // ----------------------------------------------------------------------

      // ----------------------------------------------------------------------
      default:
        MP_ASSERT_NOT_REACHED();
      // ----------------------------------------------------------------------
    }

    if (left)
    {
      _tokenizer.peek(&helper);

      if (helper.tokenType == MTOKEN_OPERATOR && mpOperatorPriority[op] < mpOperatorPriority[helper.operatorType])
      {
        result = parseExpression(&right, right, mpOperatorPriority[helper.operatorType], true);
        if (result != MRESULT_OK) { right = NULL; goto failure; }
      }

      ASTOperator* parent = new ASTOperator(_ctx.genId(), op);
      parent->setLeft(left);
      parent->setRight(right);

      left = parent;
      right = NULL;
      op = MOPERATOR_NONE;
    }
    else
    {
      left = right;
      right = NULL;
    }
  }

failure:
  if (left) delete left;
  if (right) delete right;

  *dst = NULL;
  return result;
}

} // MathPresso namespace
<|endoftext|>