fuzion-lang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

codepoint.fz


# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation.  If not, see <https://www.gnu.org/licenses/>.


# -----------------------------------------------------------------------
#
#  Tokiwa Software GmbH, Germany
#
#  Source code of Fuzion standard library feature codepoint
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

# codepoint -- represents a unicode codepoint
#
public codepoint(public val u32) : String
  pre
    debug: codepoint.valid_codepoint val
is

  # the utf8 encoded bytes for the string representation
  # of this codepoint
  #
  public redef utf8 Sequence u8 =>
    if      codepoint.utf8_encoded_in_one_byte   .contains val then [ val.low8bits ]
    else if codepoint.utf8_encoded_in_two_bytes  .contains val then [ (u32 0xc0 | (val >>  6) & 0x1f).low8bits,
                                                                      (u32 0x80 |  val        & 0x3f).low8bits ]
    else if codepoint.utf8_encoded_in_three_bytes.contains val then [ (u32 0xe0 | (val >> 12) & 0x1f).low8bits,
                                                                      (u32 0x80 | (val >>  6) & 0x3f).low8bits,
                                                                      (u32 0x80 |  val        & 0x3f).low8bits ]
    else if codepoint.utf8_encoded_in_four_bytes .contains val then [ (u32 0xf0 | (val >> 18) & 0x07).low8bits,
                                                                      (u32 0x80 | (val >> 12) & 0x3f).low8bits,
                                                                      (u32 0x80 | (val >>  6) & 0x3f).low8bits,
                                                                      (u32 0x80 |  val        & 0x3f).low8bits ]
    else
      fuzion.std.panic "failed to encode code point $(codepoint.this)"


  # is this an ASCII code encoded in one byte
  #
  public redef is_ascii bool => codepoint.ascii.contains val


  # is this an ASCII digit i.e. 0-9
  #
  public is_ascii_digit bool => codepoint.ascii_digit.contains val


  # is this an ASCII letter i.e. A-Z or a-z
  #
  public is_ascii_letter bool => is_lowercase_ascii_letter || is_uppercase_ascii_letter

  # is this an uppercase ASCII letter i.e. A-Z
  #
  public is_uppercase_ascii_letter bool => codepoint.A_to_Z.contains val


  # is this a lowercase ASCII letter i.e. a-z
  #
  public is_lowercase_ascii_letter bool => codepoint.a_to_z.contains val


  # is this an ASCII letter or digit i.e. A-Z, a-z or 0-9
  #
  public is_ascii_alpha_num bool => is_ascii_letter || is_ascii_digit


  # is this a codepoint reserved for utf16 surrogate pairs
  #
  public is_utf16_surrogate bool => codepoint.utf16_surrogate.contains val


  # is this codepoint guaranteed to never be a valid unicode character
  #
  public is_not_a_character bool => codepoint.not_a_character.contains val


  # shortcut to get this codepoint as a u8
  #
  public as_u8 u8
    pre debug: val.fits_in_u8
  => val.as_u8


  # range of permitted value for a codepoint
  #
  public type.range interval u32 => u32 0 .. 0x10ffff


  # range of values encoded in one byte
  #
  public type.ascii interval u32 => u32 0 .. 0x7f


  # 0 to 9
  #
  public type.ascii_digit interval u32 => u32 0x30 .. 0x39


  # A to Z (uppercase only)
  #
  public type.A_to_Z interval u32 => u32 0x41 .. 0x5A


  # a to z (lowercase only)
  #
  public type.a_to_z interval u32 => u32 0x61 .. 0x7A


  # a-z and A-Z
  # https://en.wikipedia.org/wiki/ISO_basic_Latin_alphabet
  #
  public type.latin_alphabet container.Set u32 : memoize
  =>
    const (A_to_Z ? a_to_z)


  # range of values encoded in one byte
  #
  public type.utf8_encoded_in_one_byte interval u32 => ascii


  # range of values encoded in two bytes
  #
  public type.utf8_encoded_in_two_bytes interval u32 => u32 0x80 .. 0x7ff


  # range of values encoded in three bytes
  #
  public type.utf8_encoded_in_three_bytes interval u32 => u32 0x800 .. 0xffff


  # range of values encoded in four bytes
  #
  public type.utf8_encoded_in_four_bytes interval u32 => u32 0x10000 .. 0x10ffff


  # range reserved for utf16 surrogate pairs
  #
  public type.utf16_surrogate interval u32 => u32 0xd800 .. 0xdfff


  # values guaranteed never to be a valid unicode character
  #
  public type.not_a_character interval u32 => u32 0xfffe .. 0xffff


  # 'REPLACEMENT CHARACTER' (U+FFFD)
  #
  public type.replacement_character codepoint =>
    "?"


  # does `val` denote a valid codepoint?
  #
  public type.valid_codepoint(val u32) bool =>
    codepoint.range.contains val && !codepoint.utf16_surrogate.contains val


  # create codepoint from u32
  # if result would not be a valid
  # codepoint return replacement_character
  #
  public type.create(val u32) codepoint =>
    if valid_codepoint val then codepoint val else replacement_character


  # compare two codepoints for equality
  #
  # result is true iff the codepoints have the same value
  #
  public fixed redef type.equality(a, b codepoint) bool => a.val = b.val


  # compare two codepoints
  #
  # This defines a total order over strings that is unrelated to alphabetic order.
  #
  public fixed redef type.lteq(a, b codepoint) bool => a.val <= b.val


  # the Unicode General_Category of this codepoint
  #
  # see https://www.unicode.org/reports/tr44/#General_Category_Values
  #
  public category unicode_category =>
    encodings.unicode.categories.of val


  # is this a letter, i.e., is the General_Category one of Lu, Ll, Lt, Lm or Lo?
  #
  public is_letter bool => category.is_letter


  # is this an uppercase letter, i.e., is the General_Category Lu?
  #
  # NOTE: This checks the General_Category only, not the Unicode property
  # `Uppercase`, e.g., U+24B6 CIRCLED LATIN CAPITAL LETTER A is not Lu - uppercase letter
  # (it is So).  No locale specific rules are applied.
  #
  public is_uppercase_letter bool => category = unicode_categories.uppercase_letter


  # is this a lowercase letter, i.e., is the General_Category Ll?
  #
  # NOTE: This checks the General_Category only, not the Unicode property
  # `Uppercase`, e.g., U+24B6 CIRCLED LATIN CAPITAL LETTER A is not Lu - uppercase letter
  # (it is So).  No locale specific rules are applied.
  #
  public is_lowercase_letter bool => category = unicode_categories.lowercase_letter


  # is this a decimal digit, i.e., is the General_Category Nd?
  #
  # This includes digits of other scripts, e.g., U+0663 ARABIC-INDIC DIGIT THREE.
  #
  public is_digit bool => category = unicode_categories.decimal_number


  # is this numeric, i.e., is the General_Category one of Nd, Nl or No?
  #
  # This includes, e.g., U+216B ROMAN NUMERAL TWELVE and U+00BD VULGAR FRACTION ONE HALF.
  #
  public is_numeric bool => category.is_number


  # is this a letter or a decimal digit?
  #
  public is_alpha_num bool => is_letter || is_digit


  # is this a punctuation character, i.e., is the General_Category one of Pc, Pd, Ps, Pe, Pi, Pf or Po?
  #
  public is_punctuation bool => category.is_punctuation

last changed: 2026-10-07