~andyc/oil

61e07695461bbe7b6989a3331fca16f8b87d1e2b — Andy Chu a month ago 3cbd9c8
[oil-language] Implement "pound" char literals like #'a'

Like other char literals, they're actually integers.

TODO: Still need to document them, as well as backslash char literals.

Addresses part of issue #980.
M frontend/id_kind_def.py => frontend/id_kind_def.py +1 -1
@@ 314,7 314,7 @@ def AddKinds(spec):
      # Two variants of Octal: \377, and \0377.
      'Octal3', 'Octal4',
      'Unicode4', 'Unicode8',  # legacy
      'UBraced', # Oil
      'UBraced', 'Pound', # Oil
      'Literals',
  ])


M frontend/lexer_def.py => frontend/lexer_def.py +5 -0
@@ 510,6 510,11 @@ EXPR_CHARS = [
  R(r'\\[0rtn\\"%s]' % "'", Id.Char_OneChar),

  R(r'\\x[0-9a-fA-F]{2}', Id.Char_Hex),

  # Because 'a' is a string, we use the syntax #'a' for char literals
  # TODO: extend this to a valid utf-8 code point (rune), rather than a single
  # byte.
  R(r"#'.'", Id.Char_Pound),
  _U_BRACED_CHAR,
] 


M oil_lang/expr_eval.py => oil_lang/expr_eval.py +7 -3
@@ 232,9 232,13 @@ class OilEvaluator(object):
        # Maybe also :Symbol?
        return node.c.val

      elif id_ == Id.Char_OneChar:
        # In expression context, it's an integer
        return consts.LookupCharInt(node.c.val[1])
      # These two could be done at COMPILE TIME
      if id_ == Id.Char_OneChar:
        return consts.LookupCharInt(node.c.val[1])  # It's an integer
      if id_ == Id.Char_Pound:
        # TODO: accept UTF-8 code point instead of single byte
        byte = node.c.val[2]  # the a in #'a'
        return ord(byte)  # It's an integer

      # NOTE: We could allow Ellipsis for a[:, ...] here, but we're not using
      # it yet.

M oil_lang/expr_to_ast.py => oil_lang/expr_to_ast.py +1 -1
@@ 618,7 618,7 @@ class Transformer(object):
          Id.Expr_Float):
        return expr.Const(tok)

      if id_ in (Id.Expr_Null, Id.Expr_True, Id.Expr_False, Id.Char_OneChar):
      if id_ in (Id.Expr_Null, Id.Expr_True, Id.Expr_False, Id.Char_OneChar, Id.Char_Pound):
        return expr.Const(tok)

      raise NotImplementedError(Id_str(id_))

M oil_lang/grammar.pgen2 => oil_lang/grammar.pgen2 +3 -1
@@ 94,7 94,9 @@ atom: (
    # TODO: Allow suffixes on floats and decimals?
  | Expr_Float | Expr_DecInt | Expr_BinInt | Expr_OctInt | Expr_HexInt 

  | Char_OneChar  # \n \\ etc.
  | Char_OneChar  # char literal \n \\ etc.
  | Char_Pound    # char literal #'a' etc.

    # TODO: Expr_Symbol for %mykey

  | dq_string | sq_string

M spec/oil-expr.test.sh => spec/oil-expr.test.sh +10 -6
@@ 345,17 345,21 @@ sum 40
## END

#### Backslash char literal (is an integer)
var newline = \n
var backslash = \\
echo "$newline $backslash"
const newline = \n
const backslash = \\
const sq = \'
const dq = \"
echo "$newline $backslash $sq $dq"
## STDOUT:
10 92
10 92 39 34
## END

#### Pound char literal (is an integer)
var ch = #'a'
echo "[$ch]"
const a  = #'a'
const A = #'A'
echo "$a $A"
## STDOUT:
97 65
## END

#### Float Literals

M test/spec.sh => test/spec.sh +1 -1
@@ 926,7 926,7 @@ oil-word-eval() {
}

oil-expr() {
  sh-spec spec/oil-expr.test.sh --osh-failures-allowed 6 \
  sh-spec spec/oil-expr.test.sh --osh-failures-allowed 5 \
    $OSH_LIST "$@"
}