summaryrefslogtreecommitdiff
path: root/test/camera/configuration_set.cpp
AgeCommit message (Expand)Author
2021-10-15test: Remove using namespace in header filesHirokazu Honda
2020-10-20test: Omit extra semicolonsHirokazu Honda
2020-08-05libcamera: pipelines: Use sensor ID as camera nameNiklas Söderlund
2019-11-20test: Extract CameraTest class out of camera tests to libtestLaurent Pinchart
2019-06-10test: camera: Fix initialisationKieran Bingham
2019-05-23libcamera: camera: Add a validation API to the CameraConfiguration classLaurent Pinchart
2019-05-23libcamera: camera: Return a pointer from generateConfiguration()Laurent Pinchart
2019-05-23libcamera: Refactor the camera configuration storage and APILaurent Pinchart
2019-05-23libcamera: Use stream roles directly instead of StreamUsageLaurent Pinchart
2019-05-23libcamera: camera: Rename configureStreams() and streamConfiguration()Laurent Pinchart
2019-04-30test: Unify naming of configurations in testsLaurent Pinchart
2019-04-30libcamera: Use the Size class through libcameraLaurent Pinchart
2019-04-09libcamera: Switch to CameraConfigurationNiklas Söderlund
2019-04-05libcamera: camera: Add support for stream usagesNiklas Söderlund
2019-04-04test: camera: Remove streams argument from configurationValid()Niklas Söderlund
2019-03-14test: camera: Add setting of configuration testNiklas Söderlund
='n137' href='#n137'>137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generates a syntax tree from a Mojo IDL file."""

# Breaking parser stanzas is unhelpful so allow longer lines.
# pylint: disable=line-too-long

import os.path
import sys

from mojom import fileutil
from mojom.error import Error
from mojom.parse import ast
from mojom.parse.lexer import Lexer

fileutil.AddLocalRepoThirdPartyDirToModulePath()
from ply import lex
from ply import yacc

_MAX_ORDINAL_VALUE = 0xffffffff
_MAX_ARRAY_SIZE = 0xffffffff


class ParseError(Error):
  """Class for errors from the parser."""

  def __init__(self, filename, message, lineno=None, snippet=None):
    Error.__init__(
        self,
        filename,
        message,
        lineno=lineno,
        addenda=([snippet] if snippet else None))


# We have methods which look like they could be functions:
# pylint: disable=R0201
class Parser:
  def __init__(self, lexer, source, filename):
    self.tokens = lexer.tokens
    self.source = source
    self.filename = filename

  # Names of functions
  #
  # In general, we name functions after the left-hand-side of the rule(s) that
  # they handle. E.g., |p_foo_bar| for a rule |foo_bar : ...|.
  #
  # There may be multiple functions handling rules for the same left-hand-side;
  # then we name the functions |p_foo_bar_N| (for left-hand-side |foo_bar|),
  # where N is a number (numbered starting from 1). Note that using multiple
  # functions is actually more efficient than having single functions handle
  # multiple rules (and, e.g., distinguishing them by examining |len(p)|).
  #
  # It's also possible to have a function handling multiple rules with different
  # left-hand-sides. We do not do this.
  #
  # See http://www.dabeaz.com/ply/ply.html#ply_nn25 for more details.

  # TODO(vtl): Get rid of the braces in the module "statement". (Consider
  # renaming "module" -> "package".) Then we'll be able to have a single rule
  # for root (by making module "optional").
  def p_root_1(self, p):
    """root : """
    p[0] = ast.Mojom(None, ast.ImportList(), [])

  def p_root_2(self, p):
    """root : root module"""
    if p[1].module is not None:
      raise ParseError(
          self.filename,
          "Multiple \"module\" statements not allowed:",
          p[2].lineno,
          snippet=self._GetSnippet(p[2].lineno))
    if p[1].import_list.items or p[1].definition_list:
      raise ParseError(
          self.filename,
          "\"module\" statements must precede imports and definitions:",
          p[2].lineno,
          snippet=self._GetSnippet(p[2].lineno))
    p[0] = p[1]
    p[0].module = p[2]

  def p_root_3(self, p):
    """root : root import"""
    if p[1].definition_list:
      raise ParseError(
          self.filename,
          "\"import\" statements must precede definitions:",
          p[2].lineno,
          snippet=self._GetSnippet(p[2].lineno))
    p[0] = p[1]
    p[0].import_list.Append(p[2])

  def p_root_4(self, p):
    """root : root definition"""
    p[0] = p[1]
    p[0].definition_list.append(p[2])

  def p_import(self, p):
    """import : attribute_section IMPORT STRING_LITERAL SEMI"""
    # 'eval' the literal to strip the quotes.
    # TODO(vtl): This eval is dubious. We should unquote/unescape ourselves.
    p[0] = ast.Import(
        p[1], eval(p[3]), filename=self.filename, lineno=p.lineno(2))

  def p_module(self, p):
    """module : attribute_section MODULE identifier_wrapped SEMI"""
    p[0] = ast.Module(p[3], p[1], filename=self.filename, lineno=p.lineno(2))

  def p_definition(self, p):
    """definition : struct
                  | union
                  | interface
                  | enum
                  | const
                  | feature"""
    p[0] = p[1]

  def p_attribute_section_1(self, p):
    """attribute_section : """
    p[0] = None

  def p_attribute_section_2(self, p):
    """attribute_section : LBRACKET attribute_list RBRACKET"""
    p[0] = p[2]

  def p_attribute_list_1(self, p):
    """attribute_list : """
    p[0] = ast.AttributeList()

  def p_attribute_list_2(self, p):
    """attribute_list : nonempty_attribute_list"""
    p[0] = p[1]

  def p_nonempty_attribute_list_1(self, p):
    """nonempty_attribute_list : attribute"""
    p[0] = ast.AttributeList(p[1])

  def p_nonempty_attribute_list_2(self, p):
    """nonempty_attribute_list : nonempty_attribute_list COMMA attribute"""
    p[0] = p[1]
    p[0].Append(p[3])

  def p_attribute_1(self, p):
    """attribute : name_wrapped EQUALS identifier_wrapped"""
    p[0] = ast.Attribute(p[1],
                         p[3][1],
                         filename=self.filename,
                         lineno=p.lineno(1))

  def p_attribute_2(self, p):
    """attribute : name_wrapped EQUALS evaled_literal
                 | name_wrapped EQUALS name_wrapped"""
    p[0] = ast.Attribute(p[1], p[3], filename=self.filename, lineno=p.lineno(1))

  def p_attribute_3(self, p):
    """attribute : name_wrapped"""
    p[0] = ast.Attribute(p[1], True, filename=self.filename, lineno=p.lineno(1))

  def p_evaled_literal(self, p):
    """evaled_literal : literal"""
    # 'eval' the literal to strip the quotes. Handle keywords "true" and "false"
    # specially since they cannot directly be evaluated to python boolean
    # values.
    if p[1] == "true":
      p[0] = True
    elif p[1] == "false":
      p[0] = False
    else:
      p[0] = eval(p[1])

  def p_struct_1(self, p):
    """struct : attribute_section STRUCT name_wrapped LBRACE struct_body RBRACE SEMI"""
    p[0] = ast.Struct(p[3], p[1], p[5])

  def p_struct_2(self, p):
    """struct : attribute_section STRUCT name_wrapped SEMI"""
    p[0] = ast.Struct(p[3], p[1], None)

  def p_struct_body_1(self, p):
    """struct_body : """
    p[0] = ast.StructBody()

  def p_struct_body_2(self, p):
    """struct_body : struct_body const
                   | struct_body enum
                   | struct_body struct_field"""
    p[0] = p[1]
    p[0].Append(p[2])

  def p_struct_field(self, p):
    """struct_field : attribute_section typename name_wrapped ordinal default SEMI"""
    p[0] = ast.StructField(p[3], p[1], p[4], p[2], p[5])

  def p_feature(self, p):
    """feature : attribute_section FEATURE NAME LBRACE feature_body RBRACE SEMI"""
    p[0] = ast.Feature(p[3], p[1], p[5])

  def p_feature_body_1(self, p):
    """feature_body : """
    p[0] = ast.FeatureBody()

  def p_feature_body_2(self, p):
    """feature_body : feature_body const"""
    p[0] = p[1]
    p[0].Append(p[2])

  def p_union(self, p):
    """union : attribute_section UNION name_wrapped LBRACE union_body RBRACE SEMI"""
    p[0] = ast.Union(p[3], p[1], p[5])

  def p_union_body_1(self, p):
    """union_body : """
    p[0] = ast.UnionBody()

  def p_union_body_2(self, p):
    """union_body : union_body union_field"""
    p[0] = p[1]
    p[1].Append(p[2])

  def p_union_field(self, p):
    """union_field : attribute_section typename name_wrapped ordinal SEMI"""
    p[0] = ast.UnionField(p[3], p[1], p[4], p[2])

  def p_default_1(self, p):
    """default : """
    p[0] = None

  def p_default_2(self, p):
    """default : EQUALS constant"""
    p[0] = p[2]

  def p_interface(self, p):
    """interface : attribute_section INTERFACE name_wrapped LBRACE interface_body RBRACE SEMI"""
    p[0] = ast.Interface(p[3], p[1], p[5])

  def p_interface_body_1(self, p):
    """interface_body : """
    p[0] = ast.InterfaceBody()

  def p_interface_body_2(self, p):
    """interface_body : interface_body const
                      | interface_body enum
                      | interface_body method"""
    p[0] = p[1]
    p[0].Append(p[2])

  def p_response_1(self, p):
    """response : """
    p[0] = None

  def p_response_2(self, p):
    """response : RESPONSE LPAREN parameter_list RPAREN"""
    p[0] = p[3]

  def p_method(self, p):
    """method : attribute_section name_wrapped ordinal LPAREN parameter_list RPAREN response SEMI"""
    p[0] = ast.Method(p[2], p[1], p[3], p[5], p[7])

  def p_parameter_list_1(self, p):
    """parameter_list : """
    p[0] = ast.ParameterList()

  def p_parameter_list_2(self, p):
    """parameter_list : nonempty_parameter_list"""
    p[0] = p[1]

  def p_nonempty_parameter_list_1(self, p):
    """nonempty_parameter_list : parameter"""
    p[0] = ast.ParameterList(p[1])

  def p_nonempty_parameter_list_2(self, p):
    """nonempty_parameter_list : nonempty_parameter_list COMMA parameter"""
    p[0] = p[1]
    p[0].Append(p[3])

  def p_parameter(self, p):
    """parameter : attribute_section typename name_wrapped ordinal"""
    p[0] = ast.Parameter(
        p[3], p[1], p[4], p[2], filename=self.filename, lineno=p.lineno(3))

  def p_typename(self, p):
    """typename : nonnullable_typename QSTN
                | nonnullable_typename"""
    if len(p) == 2:
      p[0] = p[1]
    else:
      p[0] = p[1] + "?"

  def p_nonnullable_typename(self, p):
    """nonnullable_typename : basictypename
                            | array
                            | fixed_array
                            | associative_array"""
    p[0] = p[1]

  def p_basictypename(self, p):
    """basictypename : remotetype
                     | receivertype
                     | associatedremotetype
                     | associatedreceivertype
                     | identifier
                     | ASSOCIATED identifier
                     | handletype"""
    if len(p) == 2:
      p[0] = p[1]
    else:
      p[0] = "asso<" + p[2] + ">"

  def p_remotetype(self, p):
    """remotetype : PENDING_REMOTE LANGLE identifier RANGLE"""
    p[0] = "rmt<%s>" % p[3]

  def p_receivertype(self, p):
    """receivertype : PENDING_RECEIVER LANGLE identifier RANGLE"""
    p[0] = "rcv<%s>" % p[3]

  def p_associatedremotetype(self, p):
    """associatedremotetype : PENDING_ASSOCIATED_REMOTE LANGLE identifier RANGLE"""
    p[0] = "rma<%s>" % p[3]

  def p_associatedreceivertype(self, p):
    """associatedreceivertype : PENDING_ASSOCIATED_RECEIVER LANGLE identifier RANGLE"""
    p[0] = "rca<%s>" % p[3]

  def p_handletype(self, p):
    """handletype : HANDLE
                  | HANDLE LANGLE name_wrapped RANGLE"""
    if len(p) == 2:
      p[0] = p[1]
    else:
      if p[3] not in ('data_pipe_consumer', 'data_pipe_producer',
                      'message_pipe', 'shared_buffer', 'platform'):
        # Note: We don't enable tracking of line numbers for everything, so we
        # can't use |p.lineno(3)|.
        raise ParseError(
            self.filename,
            "Invalid handle type %r:" % p[3],
            lineno=p.lineno(1),
            snippet=self._GetSnippet(p.lineno(1)))
      p[0] = "handle<" + p[3] + ">"

  def p_array(self, p):
    """array : ARRAY LANGLE typename RANGLE"""
    p[0] = p[3] + "[]"

  def p_fixed_array(self, p):
    """fixed_array : ARRAY LANGLE typename COMMA INT_CONST_DEC RANGLE"""
    value = int(p[5])
    if value == 0 or value > _MAX_ARRAY_SIZE:
      raise ParseError(
          self.filename,
          "Fixed array size %d invalid:" % value,
          lineno=p.lineno(5),
          snippet=self._GetSnippet(p.lineno(5)))
    p[0] = p[3] + "[" + p[5] + "]"

  def p_associative_array(self, p):
    """associative_array : MAP LANGLE identifier COMMA typename RANGLE"""
    p[0] = p[5] + "{" + p[3] + "}"

  def p_ordinal_1(self, p):
    """ordinal : """
    p[0] = None

  def p_ordinal_2(self, p):
    """ordinal : ORDINAL"""
    value = int(p[1][1:])
    if value > _MAX_ORDINAL_VALUE:
      raise ParseError(
          self.filename,
          "Ordinal value %d too large:" % value,
          lineno=p.lineno(1),
          snippet=self._GetSnippet(p.lineno(1)))
    p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))

  def p_enum_1(self, p):
    """enum : attribute_section ENUM name_wrapped LBRACE enum_value_list RBRACE SEMI
            | attribute_section ENUM name_wrapped LBRACE \
                    nonempty_enum_value_list COMMA RBRACE SEMI"""
    p[0] = ast.Enum(
        p[3], p[1], p[5], filename=self.filename, lineno=p.lineno(2))

  def p_enum_2(self, p):
    """enum : attribute_section ENUM name_wrapped SEMI"""
    p[0] = ast.Enum(
        p[3], p[1], None, filename=self.filename, lineno=p.lineno(2))

  def p_enum_value_list_1(self, p):
    """enum_value_list : """
    p[0] = ast.EnumValueList()

  def p_enum_value_list_2(self, p):
    """enum_value_list : nonempty_enum_value_list"""
    p[0] = p[1]

  def p_nonempty_enum_value_list_1(self, p):
    """nonempty_enum_value_list : enum_value"""
    p[0] = ast.EnumValueList(p[1])

  def p_nonempty_enum_value_list_2(self, p):
    """nonempty_enum_value_list : nonempty_enum_value_list COMMA enum_value"""
    p[0] = p[1]
    p[0].Append(p[3])

  def p_enum_value(self, p):
    """enum_value : attribute_section name_wrapped
                  | attribute_section name_wrapped EQUALS int
                  | attribute_section name_wrapped EQUALS identifier_wrapped"""
    p[0] = ast.EnumValue(
        p[2],
        p[1],
        p[4] if len(p) == 5 else None,
        filename=self.filename,
        lineno=p.lineno(2))

  def p_const(self, p):
    """const : attribute_section CONST typename name_wrapped EQUALS constant SEMI"""
    p[0] = ast.Const(p[4], p[1], p[3], p[6])

  def p_constant(self, p):
    """constant : literal
                | identifier_wrapped"""
    p[0] = p[1]

  def p_identifier_wrapped(self, p):
    """identifier_wrapped : identifier"""
    p[0] = ('IDENTIFIER', p[1])

  # TODO(vtl): Make this produce a "wrapped" identifier (probably as an
  # |ast.Identifier|, to be added) and get rid of identifier_wrapped.
  def p_identifier(self, p):
    """identifier : name_wrapped
                  | name_wrapped DOT identifier"""
    p[0] = ''.join(p[1:])

  # Allow 'feature' to be a name literal not just a keyword.
  def p_name_wrapped(self, p):
    """name_wrapped : NAME
                    | FEATURE"""
    p[0] = p[1]

  def p_literal(self, p):
    """literal : int
               | float
               | TRUE
               | FALSE
               | DEFAULT
               | STRING_LITERAL"""
    p[0] = p[1]

  def p_int(self, p):
    """int : int_const
           | PLUS int_const
           | MINUS int_const"""
    p[0] = ''.join(p[1:])

  def p_int_const(self, p):
    """int_const : INT_CONST_DEC
                 | INT_CONST_HEX"""
    p[0] = p[1]

  def p_float(self, p):
    """float : FLOAT_CONST
             | PLUS FLOAT_CONST
             | MINUS FLOAT_CONST"""
    p[0] = ''.join(p[1:])

  def p_error(self, e):
    if e is None:
      # Unexpected EOF.
      # TODO(vtl): Can we figure out what's missing?
      raise ParseError(self.filename, "Unexpected end of file")

    if e.value == 'feature':
      raise ParseError(self.filename,
                       "`feature` is reserved for a future mojom keyword",
                       lineno=e.lineno,
                       snippet=self._GetSnippet(e.lineno))

    raise ParseError(
        self.filename,
        "Unexpected %r:" % e.value,
        lineno=e.lineno,
        snippet=self._GetSnippet(e.lineno))

  def _GetSnippet(self, lineno):
    return self.source.split('\n')[lineno - 1]


def Parse(source, filename):
  """Parse source file to AST.

  Args:
    source: The source text as a str (Python 2 or 3) or unicode (Python 2).
    filename: The filename that |source| originates from.

  Returns:
    The AST as a mojom.parse.ast.Mojom object.
  """
  lexer = Lexer(filename)
  parser = Parser(lexer, source, filename)

  lex.lex(object=lexer)
  yacc.yacc(module=parser, debug=0, write_tables=0)

  tree = yacc.parse(source)
  return tree