Enso's Text Type (#1166)

This commit is contained in:
Marcin Kostrzewa 2020-09-30 13:33:57 +02:00 committed by GitHub
parent bd9689a2b4
commit a1748c3978
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 2631 additions and 711 deletions

View File

@ -188,6 +188,12 @@ jobs:
run: |
cp enso.exe ${{ env.LAUNCHER_DIST_DIR }}/bin/
- name: Build Base Java Extensions
shell: bash
run: |
cd std-bits
mvn package
# The way artifacts are uploaded currently does not preserve the
# executable bits for Unix. However putting artifacts into a ZIP would
# create a twice nested ZIP file. For now, users downloading artifacts

View File

@ -320,6 +320,12 @@ jobs:
run: |
cp enso.exe ${{ env.LAUNCHER_DIST_DIR }}/bin/
- name: Build Base Java Extensions
shell: bash
run: |
cd std-bits
mvn package
# The way artifacts are uploaded currently does not preserve the
# executable bits for Unix. However putting artifacts into a ZIP would
# create a twice nested ZIP file. For now, users downloading artifacts

2
.gitignore vendored
View File

@ -13,6 +13,7 @@ graal_dumps/
target/
*.class
*.log
.java-version
##########
## Rust ##
@ -62,6 +63,7 @@ package-lock.json
*.swp
.projections.json
.nvmrc
*.iml
############################
## Rendered Documentation ##

View File

@ -4,6 +4,7 @@ import com.typesafe.sbt.SbtLicenseReport.autoImportImpl.{
licenseReportNotes,
licenseReportStyleRules
}
import scala.sys.process._
import org.enso.build.BenchTasks._
import org.enso.build.WithDebugCommand
import sbt.Keys.{libraryDependencies, scalacOptions}
@ -893,6 +894,21 @@ lazy val runtime = (project in file("engine/runtime"))
.dependsOn(Def.task { (Compile / sourceManaged).value.mkdirs })
.value
)
.settings(
(Test / compile) := (Test / compile)
.dependsOn(Def.task {
val cmd = Seq("mvn", "package", "-f", "std-bits")
val exitCode = if (sys.props("os.name").toLowerCase().contains("win")) {
(Seq("cmd", "/c") ++ cmd).!
} else {
cmd.!
}
if (exitCode != 0) {
throw new RuntimeException("std-bits build failed.")
}
})
.value
)
.settings(
logBuffered := false,
bench := (test in Benchmark).tag(Exclusive).value,
@ -994,7 +1010,7 @@ lazy val runner = project
"commons-cli" % "commons-cli" % commonsCliVersion,
"com.monovore" %% "decline" % declineVersion,
"org.jline" % "jline" % jlineVersion,
"org.typelevel" %% "cats-core" % catsVersion,
"org.typelevel" %% "cats-core" % catsVersion
),
connectInput in run := true
)

View File

@ -1,13 +1,15 @@
import Base.List
import Base.Vector
import Base.Number.Extensions
from Builtins import Unit, Number, Integer
import Base.Text.Extensions
from Builtins import Unit, Number, Integer, Any, True, False
from Builtins export all
from Base.List export Nil, Cons
from Base.Vector export Vector
from Base.Number.Extensions export all hiding Math
from Base.Text.Extensions export Text
## Represents a right-exclusive range of integer values.
type Range
@ -44,9 +46,22 @@ type Range
res = it initial this.start this.end
res
## Checks whether `predicate` is satisfied for every number in this range.
every predicate =
it start end = if start==end then True else
r = predicate start
if r then (it start+1 end) else False
res = it this.start this.end
res
type Math
## The mathematical constant pi, equal to the ratio of a circle circumference
to its diameter.
Math.pi : Decimal
Math.pi = 3.141592653589793
## Equality definition for types defining `==`.
TODO remove when operators can be defined in-language.
Any.equals that = this == that

View File

@ -82,4 +82,4 @@ Integer.upto n = Range this n
Returns `True` when `this` and `that` are at most `epsilon` apart.
Number.equals : Number -> Number -> Boolean
Number.equals that epsilon = (this - that).abs <= epsilon
Number.equals that epsilon=0.0 = (this - that).abs <= epsilon

View File

@ -1,4 +1,5 @@
from Builtins import System
from Base import all
type Os
type Linux
@ -10,7 +11,7 @@ type Os
Create an Os object from text.
from_text: Text -> Os
from_text os =
if os == "linux" then Linux else if os == "macos" then MacOS else if os == "windows" then Windows else Unknown
if os.equals "linux" then Linux else if os.equals "macos" then MacOS else if os.equals "windows" then Windows else Unknown
## Return the type of operating system.

View File

@ -22,16 +22,18 @@ Suite.is_fail = this.specs.any is_fail
type Assertion
type Success
type Failure message
type Pending
is_fail = case this of
Success -> False
Failure _ -> True
Pending -> False
## Fail a test with the given message.
fail message = Panic.throw (Failure message)
## Asserts that `this` value is equal to the expected value.
Any.should_equal that = case this == that of
Any.should_equal that = case this.equals that of
True -> Success
False ->
msg = this.to_text + " did not equal " + that.to_text + "."
@ -64,6 +66,8 @@ Spec.print_report =
Failure msg ->
IO.print_err (" - [FAILED] " + behavior.name)
IO.print_err (" Reason: " + msg)
Pending ->
IO.print_err (" - [PENDING] " + behavior.name)
## Creates a new test group, desribing properties of the object
described by `this`.
@ -93,16 +97,21 @@ Text.describe ~behaviors =
2+3 . should_equal 5
it "should define multiplication" <|
2*3 . should_equal 6
Text.it ~behavior =
Text.it ~behavior pending=False =
result = if pending then Pending else here.run_spec behavior
spec = State.get Spec
new_spec = Spec spec.name (Cons (Behavior this result) spec.behaviors)
State.put Spec new_spec
run_spec ~behavior =
maybeExc = case Panic.recover behavior of
_ -> Success
result = maybeExc.catch ex->
case ex of
Failure _ -> ex
_ -> Failure ("Unexpected error has been thrown: " + ex.to_text)
new_spec = Spec spec.name (Cons (Behavior this result) spec.behaviors)
State.put Spec new_spec
result
## Runs a suite of tests, consisting of multiple `describe` blocks.

View File

@ -0,0 +1,105 @@
from Base import all
from Builtins import Text, Prim_Text_Helpers
from Builtins export Text
polyglot java import com.ibm.icu.text.BreakIterator
polyglot java import org.enso.base.Text_Utils
## Applies `function` to each character in `this`.
A character is defined as an Extended Grapheme Cluster, see
[Unicode Standard Annex #29](https://unicode.org/reports/tr29/).
This is the smallest unit that still has semantic meaning in most
text-processing applications.
Text.each : (Text -> Any) -> Unit
Text.each function =
iterator = BreakIterator.getCharacterInstance []
iterator.setText [this]
fst = iterator.first []
nxt = iterator.next []
iterate prev nxt = if nxt == -1 then Unit else
function (Text_Utils.substring [this, prev, nxt])
next_nxt = iterator.next []
iterate nxt next_nxt
iterate fst nxt
Unit
## Returns a vector containing all characters in the given text.
A character is defined as an Extended Grapheme Cluster, see
[Unicode Standard Annex #29](https://unicode.org/reports/tr29/).
This is the smallest unit that still has semantic meaning in most
text-processing applications.
Text.characters : Vector
Text.characters =
bldr = Vector.new_builder
this.each bldr.append
r = bldr.to_vector
r
## Takes a separator string and returns a vector resulting from splitting
`this` on each occurence of `separator`.
> Example
In the following example, we'll split the text into a vector of
comma-separated items:
"ham,eggs,cheese,tomatoes".split_at ","
The code above returns:
["ham", "eggs", "cheese", "tomatoes"]
Text.split_at : Text -> Vector
Text.split_at separator =
Vector.from_polyglot_array (Text_Utils.split_at [this, separator])
## Checks whether `this` is equal to `that`.
The definition of equality includes Unicode canonicalization. I.e. two texts
are equal if they are identical after canonical decomposition. This ensures
that different ways of expressing the same character in the underlying
binary representation are considered equal.
> Example
The string 'é' (i.e. the character U+00E9, LATIN SMALL LETTER E WITH
ACUTE) is canonically the same as the string 'e\u0301' (i.e. the letter
`e` followed by U+0301, COMBINING ACUTE ACCENT). Therefore:
('é'.equals 'e\u0301') == True
Text.equals : Text -> Boolean
Text.equals that = Text_Utils.equals [this, that]
## Returns a vector containing bytes representing the UTF-8 encoding of the
input text.
This is useful for low-level operations, such as binary data encoding and
decoding.
Text.utf_8 : Vector
Text.utf_8 = Vector.from_polyglot_array (Text_Utils.get_bytes [this])
## Takes an array of bytes and returns Text resulting from decoding it as
UTF-8.
This is useful for low-level operations, such as binary data encoding and
decoding.
Text.from_utf_8 : Vector -> Text
Text.from_utf_8 bytes = Text_Utils.from_utf_8 [bytes.to_array]
## Returns a vector containing integers representing the Unicode codepoints of
the input text.
This is useful for low-level operations, such as binary data encoding and
decoding.
Text.codepoints : Vector
Text.codepoints =
Vector.from_polyglot_array (Text_Utils.get_codepoints [this])
## Takes an array of numbers and returns the text resulting from interpreting it
as a sequence of Unicode codepoints.
This is useful for low-level operations, such as binary data encoding and
decoding.
Text.from_codepoints : Vector -> Text
Text.from_codepoints codepoints = Text_Utils.from_codepoints [codepoints.to_array]

View File

@ -25,6 +25,40 @@ type Vector
at : Number -> Any
at index = this.to_array.at index
## Creates a new vector builder instance.
A vector builder is a mutable data structure, that allows for gathering
a number of elements and then converting them into a vector. This is
particularly useful when the number of elements is not known upfront.
> Example
In the following example we'll read items from the standard input,
until the string "end" is entered by the user and then return a vector
containing all items.
from Base import all
main =
builder = Vector.new_builder
do_read =
item = IO.readln
if item.equals "end" then Unit else
builder.append item
do_read
do_read
vec = builder.to_vector
IO.println vec
new_builder : Builder
new_builder = Builder.new
## Converts a polyglot value representing an array into a vector. This is
useful when wrapping polyglot APIs for further use in Enso.
from_polyglot_array : Any -> Vector
from_polyglot_array arr =
a = Array.new arr.length
0.upto arr.length . each i->
a.set_at i (arr.at i)
Vector a
## Returns the number of elements stored in this vector.
length : Number
length = this.to_array.length
@ -104,3 +138,71 @@ type Vector
folder = str -> ix -> str + ", " + (arr.at ix).to_text
tail_elems = 1.upto arr.length . fold "" folder
"[" + (arr.at 0 . to_text) + tail_elems + "]"
## Checks whether this vector is equal to `that`. Two vectors are considered
equal, when they have the same length and their items are pairwise equal.
equals : Vector -> Boolean
equals that =
arr1 = this.to_array
arr2 = that.to_array
eq_at i = (arr1.at i) . equals (arr2.at i)
r = if arr1.length == arr2.length then 0.upto arr1.length . every eq_at else False
r
## A builder type for Enso vectors.
A vector builder is a mutable data structure, that allows to gather a
number of elements and then convert them to a vector. This is
particularly useful when the number of elements is not known upfront.
> Example
In the following example we'll read items from the standard input,
until the string "end" is entered by the user and then return a vector
containing all items.
from Base import all
main =
builder = Vector.new_builder
do_read =
item = IO.readln
if item.equals "end" then Unit else
builder.append item
do_read
do_read
vec = builder.to_vector
IO.println vec
type Builder
type Builder to_array length
## Creates a new builder.
new = Builder (Array.new 1) 0
## Returns the current capacity (i.e. the size of the underlying storage)
of this builder.
capacity = this.to_array.length
## Appends a new element into this builder.
append : Any -> Unit
append item = case this.capacity > this.length of
True ->
this.to_array.set_at this.length item
Unsafe.set_atom_field this 1 (this.length + 1)
False ->
old_array = this.to_array
new_array = Array.new old_array.length*2
0.upto this.length . each i->
new_array.set_at i (old_array.at i)
Unit
Unsafe.set_atom_field this 0 new_array
this.append item
Unit
## Converts this builder to a vector containing all the appended elements.
to_vector : Vector
to_vector =
old_array = this.to_array
new_array = Array.new this.length
0.upto this.length . each i->
new_array.set_at i (old_array.at i)
Unit
Vector new_array

View File

@ -0,0 +1,3 @@
Lists of 1 third-party dependencies.
(Unicode/ICU License) ICU4J (com.ibm.icu:icu4j:67.1 - http://icu-project.org/)

View File

@ -0,0 +1,414 @@
COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
Copyright © 1991-2020 Unicode, Inc. All rights reserved.
Distributed under the Terms of Use in https://www.unicode.org/copyright.html.
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Unicode data files and any associated documentation
(the "Data Files") or Unicode software and any associated documentation
(the "Software") to deal in the Data Files or Software
without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, and/or sell copies of
the Data Files or Software, and to permit persons to whom the Data Files
or Software are furnished to do so, provided that either
(a) this copyright and permission notice appear with all copies
of the Data Files or Software, or
(b) this copyright and permission notice appear in associated
Documentation.
THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THE DATA FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in these Data Files or Software without prior
written authorization of the copyright holder.
---------------------
Third-Party Software Licenses
This section contains third-party software notices and/or additional
terms for licensed third-party software components included within ICU
libraries.
1. ICU License - ICU 1.8.1 to ICU 57.1
COPYRIGHT AND PERMISSION NOTICE
Copyright (c) 1995-2016 International Business Machines Corporation and others
All rights reserved.
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, and/or sell copies of the Software, and to permit persons
to whom the Software is furnished to do so, provided that the above
copyright notice(s) and this permission notice appear in all copies of
the Software and that both the above copyright notice(s) and this
permission notice appear in supporting documentation.
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
OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Except as contained in this notice, the name of a copyright holder
shall not be used in advertising or otherwise to promote the sale, use
or other dealings in this Software without prior written authorization
of the copyright holder.
All trademarks and registered trademarks mentioned herein are the
property of their respective owners.
2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt)
# The Google Chrome software developed by Google is licensed under
# the BSD license. Other software included in this distribution is
# provided under other licenses, as set forth below.
#
# The BSD License
# http://opensource.org/licenses/bsd-license.php
# Copyright (C) 2006-2008, Google Inc.
#
# 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 Google Inc. 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.
#
#
# The word list in cjdict.txt are generated by combining three word lists
# listed below with further processing for compound word breaking. The
# frequency is generated with an iterative training against Google web
# corpora.
#
# * Libtabe (Chinese)
# - https://sourceforge.net/project/?group_id=1519
# - Its license terms and conditions are shown below.
#
# * IPADIC (Japanese)
# - http://chasen.aist-nara.ac.jp/chasen/distribution.html
# - Its license terms and conditions are shown below.
#
# ---------COPYING.libtabe ---- BEGIN--------------------
#
# /*
# * Copyright (c) 1999 TaBE Project.
# * Copyright (c) 1999 Pai-Hsiang Hsiao.
# * 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 the TaBE Project 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
# * REGENTS 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.
# */
#
# /*
# * Copyright (c) 1999 Computer Systems and Communication Lab,
# * Institute of Information Science, Academia
# * Sinica. 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 the Computer Systems and Communication Lab
# * 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
# * REGENTS 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.
# */
#
# Copyright 1996 Chih-Hao Tsai @ Beckman Institute,
# University of Illinois
# c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4
#
# ---------------COPYING.libtabe-----END--------------------------------
#
#
# ---------------COPYING.ipadic-----BEGIN-------------------------------
#
# Copyright 2000, 2001, 2002, 2003 Nara Institute of Science
# and Technology. All Rights Reserved.
#
# Use, reproduction, and distribution of this software is permitted.
# Any copy of this software, whether in its original form or modified,
# must include both the above copyright notice and the following
# paragraphs.
#
# Nara Institute of Science and Technology (NAIST),
# the copyright holders, disclaims all warranties with regard to this
# software, including all implied warranties of merchantability and
# fitness, in no event shall NAIST be liable for
# any special, indirect or consequential damages or any damages
# whatsoever resulting from loss of use, data or profits, whether in an
# action of contract, negligence or other tortuous action, arising out
# of or in connection with the use or performance of this software.
#
# A large portion of the dictionary entries
# originate from ICOT Free Software. The following conditions for ICOT
# Free Software applies to the current dictionary as well.
#
# Each User may also freely distribute the Program, whether in its
# original form or modified, to any third party or parties, PROVIDED
# that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear
# on, or be attached to, the Program, which is distributed substantially
# in the same form as set out herein and that such intended
# distribution, if actually made, will neither violate or otherwise
# contravene any of the laws and regulations of the countries having
# jurisdiction over the User or the intended distribution itself.
#
# NO WARRANTY
#
# The program was produced on an experimental basis in the course of the
# research and development conducted during the project and is provided
# to users as so produced on an experimental basis. Accordingly, the
# program is provided without any warranty whatsoever, whether express,
# implied, statutory or otherwise. The term "warranty" used herein
# includes, but is not limited to, any warranty of the quality,
# performance, merchantability and fitness for a particular purpose of
# the program and the nonexistence of any infringement or violation of
# any right of any third party.
#
# Each user of the program will agree and understand, and be deemed to
# have agreed and understood, that there is no warranty whatsoever for
# the program and, accordingly, the entire risk arising from or
# otherwise connected with the program is assumed by the user.
#
# Therefore, neither ICOT, the copyright holder, or any other
# organization that participated in or was otherwise related to the
# development of the program and their respective officials, directors,
# officers and other employees shall be held liable for any and all
# damages, including, without limitation, general, special, incidental
# and consequential damages, arising out of or otherwise in connection
# with the use or inability to use the program or any product, material
# or result produced or otherwise obtained by using the program,
# regardless of whether they have been advised of, or otherwise had
# knowledge of, the possibility of such damages at any time during the
# project or thereafter. Each user will be deemed to have agreed to the
# foregoing by his or her commencement of use of the program. The term
# "use" as used herein includes, but is not limited to, the use,
# modification, copying and distribution of the program and the
# production of secondary products from the program.
#
# In the case where the program, whether in its original form or
# modified, was distributed or delivered to or received by a user from
# any person, organization or entity other than ICOT, unless it makes or
# grants independently of ICOT any specific warranty to the user in
# writing, such person, organization or entity, will also be exempted
# from and not be held liable to the user for any such damages as noted
# above as far as the program is concerned.
#
# ---------------COPYING.ipadic-----END----------------------------------
3. Lao Word Break Dictionary Data (laodict.txt)
# Copyright (c) 2013 International Business Machines Corporation
# and others. All Rights Reserved.
#
# Project: https://github.com/veer66/lao-dictionary
# Dictionary: https://github.com/veer66/lao-dictionary/blob/master/Lao-Dictionary.txt
# License: https://github.com/veer66/lao-dictionary/blob/master/Lao-Dictionary-LICENSE.txt
# (copied below)
#
# This file is derived from the above dictionary, with slight
# modifications.
# ----------------------------------------------------------------------
# Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell.
# 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.
#
#
# 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 HOLDER 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.
# --------------------------------------------------------------------------
4. Burmese Word Break Dictionary Data (burmesedict.txt)
# Copyright (c) 2014 International Business Machines Corporation
# and others. All Rights Reserved.
#
# This list is part of a project hosted at:
# github.com/kanyawtech/myanmar-karen-word-lists
#
# --------------------------------------------------------------------------
# Copyright (c) 2013, LeRoy Benjamin Sharon
# 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 Myanmar Karen Word Lists, 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 HOLDER 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.
# --------------------------------------------------------------------------
5. Time Zone Database
ICU uses the public domain data and code derived from Time Zone
Database for its time zone support. The ownership of the TZ database
is explained in BCP 175: Procedure for Maintaining the Time Zone
Database section 7.
# 7. Database Ownership
#
# The TZ database itself is not an IETF Contribution or an IETF
# document. Rather it is a pre-existing and regularly updated work
# that is in the public domain, and is intended to remain in the
# public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do
# not apply to the TZ Database or contributions that individuals make
# to it. Should any claims be made and substantiated against the TZ
# Database, the organization that is providing the IANA
# Considerations defined in this RFC, under the memorandum of
# understanding with the IETF, currently ICANN, may act in accordance
# with all competent court orders. No ownership claims will be made
# by ICANN or the IETF Trust on the database or the code. Any person
# making a contribution to the database or code waives all rights to
# future claims in that contribution or in the TZ Database.
6. Google double-conversion
Copyright 2006-2011, the V8 project authors. 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 Google Inc. 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.

View File

@ -138,6 +138,7 @@ In order to build and run Enso you will need the following tools:
- [sbt](https://www.scala-sbt.org/) with the same version as specified in
[`project/build.properties`](../project/build.properties).
- [Maven](https://maven.apache.org/) with version at least 3.6.3.
- [GraalVM](https://www.graalvm.org/) with the same version as described in the
[`build.sbt`](../build.sbt) file, configured as your default JVM. GraalVM is
distributed for different Java versions, so you need a GraalVM distribution

View File

@ -92,6 +92,13 @@ files are included by the CI build within the built artifacts.
### Engine Components
#### Standard Library
The third-party licenses for Java extensions of the standard library are
gathered in the `third-party-licenses` directory in the `Base` library. The
gathering process is automatic, triggered by the `package` goal of the
associated Maven configuration file.
> The actionables for this section are:
>
> - The engine components as distributed as a JAR archive that everyone can

View File

@ -4,6 +4,7 @@ import java.io.{InputStream, OutputStream, PrintStream, PrintWriter, Writer}
import java.util.Scanner
import org.enso.polyglot.debugger.{ReplExecutor, SessionManager}
import org.jline.reader.impl.DefaultParser
import org.jline.reader.{LineReader, LineReaderBuilder}
import org.jline.terminal.{Terminal, TerminalBuilder}
@ -98,8 +99,10 @@ case class SimpleReplIO(in: InputStream, out: OutputStream) extends ReplIO {
case class TerminalIO() extends ReplIO {
private val terminal: Terminal =
TerminalBuilder.builder().system(true).build()
private val parser: DefaultParser = new DefaultParser()
parser.setEscapeChars(null)
private val lineReader: LineReader =
LineReaderBuilder.builder().terminal(terminal).build()
LineReaderBuilder.builder().parser(parser).terminal(terminal).build()
/**
* Ask user for a line of input, using given prompt

View File

@ -1,11 +1,5 @@
package org.enso.interpreter.bench;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.xml.bind.JAXBException;
import org.openjdk.jmh.results.RunResult;
import org.openjdk.jmh.runner.BenchmarkList;
import org.openjdk.jmh.runner.BenchmarkListEntry;
@ -14,6 +8,12 @@ import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import javax.xml.bind.JAXBException;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/** Runner class for the benchmarks. Discovers, runs and reports benchmark results. */
public class BenchmarksRunner {
public static final File REPORT_FILE = new File("./bench-report.xml");

View File

@ -1,5 +1,7 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import org.enso.interpreter.bench.fixtures.semantic.RecursionFixtures;
import org.enso.interpreter.test.DefaultInterpreterRunner;

View File

@ -17,11 +17,13 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.node.expression.debug.CaptureResultScopeNode;
import org.enso.interpreter.node.expression.debug.EvalNode;
import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.scope.FramePointer;
import org.enso.interpreter.runtime.state.Stateful;
import org.enso.polyglot.debugger.DebugServerInfo;
@ -47,28 +49,28 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
@Override
protected void onCreate(Env env) {
SourceSectionFilter filter =
SourceSectionFilter.newBuilder().tagIs(DebuggerTags.AlwaysHalt.class)
.build();
SourceSectionFilter.newBuilder().tagIs(DebuggerTags.AlwaysHalt.class).build();
this.env = env;
DebuggerMessageHandler handler = new DebuggerMessageHandler();
try {
MessageEndpoint client =
env.startServer(URI.create(DebugServerInfo.URI), handler);
MessageEndpoint client = env.startServer(URI.create(DebugServerInfo.URI), handler);
if (client != null) {
handler.setClient(client);
Instrumenter instrumenter = env.getInstrumenter();
instrumenter.attachExecutionEventFactory(filter, ctx ->
new ReplExecutionEventNode(ctx, handler, env.getLogger(ReplExecutionEventNode.class)));
instrumenter.attachExecutionEventFactory(
filter,
ctx ->
new ReplExecutionEventNode(
ctx, handler, env.getLogger(ReplExecutionEventNode.class)));
} else {
env.getLogger(ReplDebuggerInstrument.class)
.warning("ReplDebuggerInstrument was initialized, " +
"but no client connected");
.warning("ReplDebuggerInstrument was initialized, " + "but no client connected");
}
} catch (MessageTransport.VetoException e) {
env.getLogger(ReplDebuggerInstrument.class)
.warning("ReplDebuggerInstrument was initialized, " +
"but client connection has been vetoed");
.warning(
"ReplDebuggerInstrument was initialized, " + "but client connection has been vetoed");
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -85,6 +87,7 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
/** The actual node that's installed as a probe on any node the instrument was launched for. */
public static class ReplExecutionEventNode extends ExecutionEventNode {
private @Child EvalNode evalNode = EvalNode.buildWithResultScopeCapture();
private @Child ToJavaStringNode toJavaStringNode = ToJavaStringNode.build();
private ReplExecutionEventNodeState nodeState;
@ -92,7 +95,8 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
private DebuggerMessageHandler handler;
private TruffleLogger logger;
private ReplExecutionEventNode(EventContext eventContext, DebuggerMessageHandler handler, TruffleLogger logger) {
private ReplExecutionEventNode(
EventContext eventContext, DebuggerMessageHandler handler, TruffleLogger logger) {
this.eventContext = eventContext;
this.handler = handler;
this.logger = logger;
@ -116,7 +120,8 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
* @return a map, where keys are variable names and values are current values of variables.
*/
public Map<String, Object> listBindings() {
Map<String, FramePointer> flatScope = nodeState.getLastScope().getLocalScope().flattenBindings();
Map<String, FramePointer> flatScope =
nodeState.getLastScope().getLocalScope().flattenBindings();
Map<String, Object> result = new HashMap<>();
for (Map.Entry<String, FramePointer> entry : flatScope.entrySet()) {
result.put(entry.getKey(), getValue(nodeState.getLastScope().getFrame(), entry.getValue()));
@ -128,20 +133,21 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
* Evaluates an arbitrary expression in the current execution context.
*
* @param expression the expression to evaluate
* @return the result of evaluating the expression or an exception that
* caused failure
* @return the result of evaluating the expression or an exception that caused failure
*/
public Either<Exception, Object> evaluate(String expression) {
ReplExecutionEventNodeState savedState = nodeState;
try {
Stateful result = evalNode.execute(nodeState.getLastScope(), nodeState.getLastState(), expression);
Stateful result =
evalNode.execute(
nodeState.getLastScope(), nodeState.getLastState(), Text.create(expression));
Object lastState = result.getState();
CaptureResultScopeNode.WithCallerInfo payload =
(CaptureResultScopeNode.WithCallerInfo) result.getValue();
CallerInfo lastScope = payload.getCallerInfo();
Object lastReturn = payload.getResult();
nodeState = new ReplExecutionEventNodeState(lastReturn, lastState, lastScope);
return new Right<>(lastReturn);
return new Right<>(formatObject(lastReturn));
} catch (Exception e) {
nodeState = savedState;
TruffleStackTrace.fillIn(e);
@ -149,6 +155,14 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
}
}
private Object formatObject(Object o) {
if (o instanceof Text) {
return toJavaStringNode.execute((Text) o);
} else {
return o;
}
}
/**
* Terminates this REPL session.
*
@ -202,8 +216,9 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
if (handler.hasClient()) {
handler.startSession(this);
} else {
logger.warning("Debugger session starting, " +
"but no client connected, will terminate the session immediately");
logger.warning(
"Debugger session starting, "
+ "but no client connected, will terminate the session immediately");
exit();
}
}
@ -211,9 +226,9 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
/**
* State of the execution node.
*
* As the execution nodes are reused by Truffle, the nested nodes share
* state. If execution of a nested node fails, to ensure consistent state of
* the parent node, its state has to be restored.
* <p>As the execution nodes are reused by Truffle, the nested nodes share state. If execution
* of a nested node fails, to ensure consistent state of the parent node, its state has to be
* restored.
*/
private static class ReplExecutionEventNodeState {
private final Object lastReturn;
@ -239,5 +254,4 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
}
}
}
}

View File

@ -9,6 +9,7 @@ import org.enso.interpreter.Constants;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.callable.dispatch.IndirectInvokeFunctionNode;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function;
@ -66,8 +67,13 @@ public abstract class InteropApplicationNode extends Node {
Object[] arguments,
@CachedContext(Language.class) Context context,
@Cached("arguments.length") int cachedArgsLength,
@Cached("buildSorter(cachedArgsLength)") InvokeFunctionNode sorterNode) {
return sorterNode.execute(function, null, state, arguments).getValue();
@Cached("buildSorter(cachedArgsLength)") InvokeFunctionNode sorterNode,
@Cached("build()") HostValueToEnsoNode hostValueToEnsoNode) {
Object[] args = new Object[cachedArgsLength];
for (int i = 0; i < cachedArgsLength; i++) {
args[i] = hostValueToEnsoNode.execute(arguments[i]);
}
return sorterNode.execute(function, null, state, args).getValue();
}
@Specialization(replaces = "callCached")
@ -75,13 +81,18 @@ public abstract class InteropApplicationNode extends Node {
Function function,
Object state,
Object[] arguments,
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode,
@Cached("build()") HostValueToEnsoNode hostValueToEnsoNode) {
Object[] args = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
args[i] = hostValueToEnsoNode.execute(arguments[i]);
}
return indirectInvokeFunctionNode
.execute(
function,
null,
state,
arguments,
args,
buildSchema(arguments.length),
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED,

View File

@ -6,6 +6,8 @@ import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.Language;
@ -18,6 +20,7 @@ import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.MethodDoesNotExistException;
import org.enso.interpreter.runtime.error.RuntimeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@ -153,7 +156,7 @@ public abstract class MethodResolverNode extends Node {
@Specialization(guards = "cachedSymbol == symbol")
Function resolveString(
UnresolvedSymbol symbol,
String self,
Text self,
@Cached(value = "symbol", allowUncached = true) UnresolvedSymbol cachedSymbol,
@Cached(value = "resolveMethodOnString(cachedSymbol)", allowUncached = true)
Function function) {
@ -190,6 +193,19 @@ public abstract class MethodResolverNode extends Node {
return function;
}
@Specialization(
guards = {"isPolyglotArrayMethod(cachedSymbol)", "arrays.hasArrayElements(array)"})
Function resolvePolyglotArray(
UnresolvedSymbol symbol,
Object array,
@CachedLibrary(limit = "3") InteropLibrary arrays,
@Cached(value = "symbol", allowUncached = true) UnresolvedSymbol cachedSymbol,
@CachedContext(Language.class) Context ctx,
@Cached(value = "resolveMethodOnPolyglotArray(cachedSymbol, ctx)", allowUncached = true)
Function function) {
return function;
}
@Specialization(guards = {"cachedSymbol == symbol", "ctx.getEnvironment().isHostObject(target)"})
Function resolveHost(
UnresolvedSymbol symbol,
@ -262,7 +278,7 @@ public abstract class MethodResolverNode extends Node {
Function resolveMethodOnString(UnresolvedSymbol symbol) {
return ensureMethodExists(
symbol.resolveFor(getBuiltins().text(), getBuiltins().any()), "Text", symbol);
symbol.resolveFor(getBuiltins().text().getText(), getBuiltins().any()), "Text", symbol);
}
Function resolveMethodOnFunction(UnresolvedSymbol symbol) {
@ -283,9 +299,23 @@ public abstract class MethodResolverNode extends Node {
Function buildHostResolver(UnresolvedSymbol symbol, Context context) {
if (symbol.getName().equals("new")) {
return context.getBuiltins().getConstructorDispatch();
return context.getBuiltins().polyglot().getConstructorDispatch();
} else if (symbol.getName().equals("to_text")) {
return context.getBuiltins().polyglot().getPolyglotToTextFunction();
} else {
return context.getBuiltins().buildPolyglotMethodDispatch(symbol);
return context.getBuiltins().polyglot().buildPolyglotMethodDispatch(symbol);
}
}
static boolean isPolyglotArrayMethod(UnresolvedSymbol symbol) {
return symbol.getName().equals("at") || symbol.getName().equals("length");
}
Function resolveMethodOnPolyglotArray(UnresolvedSymbol symbol, Context context) {
if (symbol.getName().equals("length")) {
return context.getBuiltins().polyglot().getPolyglotArrayLengthFunction();
} else {
return context.getBuiltins().polyglot().getPolyglotArrayAtFunction();
}
}

View File

@ -2,10 +2,13 @@ package org.enso.interpreter.node.expression.builtin.bool;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.data.text.Text;
@BuiltinMethod(type = "Boolean", name = "to_text", description = "Boolean to text conversion.")
public class ToTextNode extends Node {
String execute(boolean _this) {
return _this ? "True" : "False";
Text t = Text.create("True");
Text f = Text.create("False");
Text execute(boolean _this) {
return _this ? t : f;
}
}

View File

@ -5,6 +5,7 @@ import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.dsl.MonadicState;
import org.enso.interpreter.node.expression.debug.EvalNode;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.state.Stateful;
/** Root node for the builtin Debug.eval function. */
@ -21,7 +22,7 @@ public class DebugEvalNode extends Node {
}
Stateful execute(
CallerInfo callerInfo, @MonadicState Object state, Object _this, String expression) {
CallerInfo callerInfo, @MonadicState Object state, Object _this, Text expression) {
return evalNode.execute(callerInfo, state, expression);
}
}

View File

@ -7,6 +7,8 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.Constants;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
@BuiltinMethod(
@ -16,11 +18,12 @@ import org.enso.interpreter.runtime.error.PanicException;
public class GetMemberNode extends Node {
private @Child InteropLibrary library =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private @Child ToJavaStringNode toJavaStringNode = ToJavaStringNode.build();
private final BranchProfile err = BranchProfile.create();
Object execute(Object _this, Object object, String member_name) {
Object execute(Object _this, Object object, Text member_name) {
try {
return library.readMember(object, member_name);
return library.readMember(object, toJavaStringNode.execute(member_name));
} catch (UnsupportedMessageException | UnknownIdentifierException e) {
err.enter();
throw new PanicException(e.getMessage(), this);

View File

@ -5,7 +5,9 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.Constants;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
@BuiltinMethod(
@ -15,11 +17,12 @@ import org.enso.interpreter.runtime.error.PanicException;
public class InvokeNode extends Node {
private @Child InteropLibrary library =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private @Child ToJavaStringNode toJavaStringNode = ToJavaStringNode.build();
private final BranchProfile err = BranchProfile.create();
Object execute(Object _this, Object target, String name, Array arguments) {
Object execute(Object _this, Object target, Text name, Array arguments) {
try {
return library.invokeMember(target, name, arguments.getItems());
return library.invokeMember(target, toJavaStringNode.execute(name), arguments.getItems());
} catch (UnsupportedMessageException
| ArityException
| UnsupportedTypeException

View File

@ -1,11 +1,14 @@
package org.enso.interpreter.node.expression.builtin.interop.java;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.data.text.Text;
import java.io.File;
@ -20,10 +23,16 @@ public abstract class AddToClassPathNode extends Node {
}
@Specialization
Object doExecute(Object _this, String path, @CachedContext(Language.class) Context context) {
context.getEnvironment().addToHostClassPath(context.getTruffleFile(new File(path)));
Object doExecute(
Object _this,
Text path,
@CachedContext(Language.class) Context context,
@Cached("build()") ToJavaStringNode toJavaStringNode) {
context
.getEnvironment()
.addToHostClassPath(context.getTruffleFile(new File(toJavaStringNode.execute(path))));
return context.getBuiltins().unit();
}
abstract Object execute(Object _this, String path);
abstract Object execute(Object _this, Text path);
}

View File

@ -1,11 +1,14 @@
package org.enso.interpreter.node.expression.builtin.interop.java;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.data.text.Text;
@BuiltinMethod(type = "Java", name = "lookup_class", description = "Looks up a Java symbol.")
public abstract class LookupClassNode extends Node {
@ -14,9 +17,13 @@ public abstract class LookupClassNode extends Node {
}
@Specialization
Object doExecute(Object _this, String name, @CachedContext(Language.class) Context ctx) {
return ctx.getEnvironment().lookupHostSymbol(name);
Object doExecute(
Object _this,
Text name,
@CachedContext(Language.class) Context ctx,
@Cached("build()") ToJavaStringNode toJavaStringNode) {
return ctx.getEnvironment().lookupHostSymbol(toJavaStringNode.execute(name));
}
abstract Object execute(Object _this, String name);
abstract Object execute(Object _this, Text name);
}

View File

@ -0,0 +1,41 @@
package org.enso.interpreter.node.expression.builtin.interop.syntax;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.Constants;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.state.Stateful;
import org.enso.interpreter.runtime.type.TypesGen;
@BuiltinMethod(
type = "Any",
name = "<polyglot_array_length>",
description = "Returns the length of a polyglot array.")
public class ArrayLengthNode extends Node {
private @Child InteropLibrary library =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private final BranchProfile err = BranchProfile.create();
public Object execute(Object _this) {
try {
return library.getArraySize(_this);
} catch (UnsupportedMessageException e) {
err.enter();
throw new PanicException(e.getMessage(), this);
}
}
}

View File

@ -1,4 +1,4 @@
package org.enso.interpreter.node.expression.builtin.interop.generic;
package org.enso.interpreter.node.expression.builtin.interop.syntax;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
@ -10,17 +10,18 @@ import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.error.PanicException;
@BuiltinMethod(
type = "Polyglot",
name = "get_array_element",
description = "Gets an element by index from a polyglot array.")
type = "Any",
name = "<polyglot_array_at>",
description = "Returns the element of a polyglot array at a given index.")
public class GetArrayElementNode extends Node {
private @Child InteropLibrary library =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private @Child HostValueToEnsoNode hostValueToEnsoNode = HostValueToEnsoNode.build();
private final BranchProfile err = BranchProfile.create();
Object execute(Object _this, Object array, long index) {
public Object execute(Object _this, long index) {
try {
return library.readArrayElement(array, index);
return hostValueToEnsoNode.execute(library.readArrayElement(_this, index));
} catch (UnsupportedMessageException | InvalidArrayIndexException e) {
err.enter();
throw new PanicException(e.getMessage(), this);

View File

@ -1,15 +1,18 @@
package org.enso.interpreter.node.expression.builtin.interop.syntax;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.runtime.data.text.Text;
/**
* Converts a value returned by a polyglot call back to a value that can be further used within Enso
* programs.
*/
@ReportPolymorphism
@GenerateUncached
public abstract class HostValueToEnsoNode extends Node {
public static HostValueToEnsoNode build() {
return HostValueToEnsoNodeGen.create();
@ -48,6 +51,11 @@ public abstract class HostValueToEnsoNode extends Node {
return i;
}
@Specialization
Text doString(String txt) {
return Text.create(txt);
}
@Fallback
Object doOther(Object o) {
return o;

View File

@ -1,4 +1,4 @@
package org.enso.interpreter.node.expression.builtin.interop.generic;
package org.enso.interpreter.node.expression.builtin.interop.syntax;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
@ -6,20 +6,23 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.Constants;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
@BuiltinMethod(
type = "Polyglot",
name = "get_array_size",
description = "Gets the size of a polyglot array.")
public class GetArraySizeNode extends Node {
type = "Any",
name = "<to_text>",
description = "Returns human-readable representation of a polyglot object.")
public class PolyglotToTextNode extends Node {
private @Child InteropLibrary library =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private @Child InteropLibrary strings =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private final BranchProfile err = BranchProfile.create();
Object execute(Object _this, Object array) {
public Text execute(Object _this) {
try {
return library.getArraySize(array);
return Text.create(strings.asString(library.toDisplayString(_this)));
} catch (UnsupportedMessageException e) {
err.enter();
throw new PanicException(e.getMessage(), this);

View File

@ -1,12 +1,25 @@
package org.enso.interpreter.node.expression.builtin.io;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.dsl.MonadicState;
import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.state.Stateful;
import org.enso.interpreter.runtime.type.TypesGen;
import java.io.PrintStream;
@BuiltinMethod(
type = "IO",
@ -17,12 +30,53 @@ public abstract class PrintErrNode extends Node {
return PrintErrNodeGen.create();
}
abstract Object execute(Object _this, Object message);
abstract Stateful execute(
VirtualFrame frame, @MonadicState Object state, Object _this, Object message);
@Specialization
@TruffleBoundary
Object doPrint(Object self, Object message, @CachedContext(Language.class) Context ctx) {
ctx.getErr().println(message);
return ctx.getUnit().newInstance();
Stateful doPrintText(
VirtualFrame frame,
Object state,
Object self,
Text message,
@CachedContext(Language.class) Context ctx,
@Cached("build()") ToJavaStringNode toJavaStringNode) {
print(ctx.getErr(), toJavaStringNode.execute(message));
return new Stateful(state, ctx.getUnit().newInstance());
}
@Specialization(guards = "!isText(message)")
Stateful doPrint(
VirtualFrame frame,
Object state,
Object self,
Object message,
@CachedContext(Language.class) Context ctx,
@Cached("buildSymbol(ctx)") UnresolvedSymbol symbol,
@Cached("buildInvokeCallableNode()") InvokeCallableNode invokeCallableNode,
@Cached("build()") ToJavaStringNode toJavaStringNode) {
Stateful str = invokeCallableNode.execute(symbol, frame, state, new Object[] {message});
print(ctx.getErr(), toJavaStringNode.execute((Text) str.getValue()));
return new Stateful(str.getState(), ctx.getUnit().newInstance());
}
@CompilerDirectives.TruffleBoundary
private void print(PrintStream err, Object str) {
err.println(str);
}
boolean isText(Object o) {
return TypesGen.isText(o);
}
InvokeCallableNode buildInvokeCallableNode() {
return InvokeCallableNode.build(
new CallArgumentInfo[] {new CallArgumentInfo()},
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED);
}
UnresolvedSymbol buildSymbol(Context ctx) {
return UnresolvedSymbol.build("to_text", ctx.getBuiltins().getScope());
}
}

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.node.expression.builtin.io;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
@ -11,10 +12,13 @@ import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.dsl.MonadicState;
import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.state.Stateful;
import org.enso.interpreter.runtime.type.TypesGen;
@BuiltinMethod(type = "IO", name = "println", description = "Prints its argument to standard out.")
public abstract class PrintlnNode extends Node {
@ -28,18 +32,37 @@ public abstract class PrintlnNode extends Node {
VirtualFrame frame, @MonadicState Object state, Object _this, Object message);
@Specialization
Stateful doPrintText(
VirtualFrame frame,
Object state,
Object self,
Text message,
@CachedContext(Language.class) Context ctx,
@Cached("build()") ToJavaStringNode toJavaStringNode) {
print(ctx.getOut(), toJavaStringNode.execute(message));
return new Stateful(state, ctx.getUnit().newInstance());
}
@Specialization(guards = "!isText(message)")
Stateful doPrint(
VirtualFrame frame,
Object state,
Object self,
Object message,
@CachedContext(Language.class) Context ctx,
@Cached("buildSymbol(ctx)") UnresolvedSymbol symbol) {
@Cached("buildSymbol(ctx)") UnresolvedSymbol symbol,
@Cached("build()") ToJavaStringNode toJavaStringNode,
@Cached("buildInvokeCallableNode()") InvokeCallableNode invokeCallableNode) {
Stateful str = invokeCallableNode.execute(symbol, frame, state, new Object[] {message});
print(ctx.getOut(), str.getValue());
String strr = toJavaStringNode.execute((Text) str.getValue());
print(ctx.getOut(), strr);
return new Stateful(str.getState(), ctx.getUnit().newInstance());
}
boolean isText(Object o) {
return TypesGen.isText(o);
}
@CompilerDirectives.TruffleBoundary
private void print(PrintStream out, Object str) {
out.println(str);
@ -49,6 +72,13 @@ public abstract class PrintlnNode extends Node {
return UnresolvedSymbol.build("to_text", ctx.getBuiltins().getScope());
}
InvokeCallableNode buildInvokeCallableNode() {
return InvokeCallableNode.build(
new CallArgumentInfo[] {new CallArgumentInfo()},
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED);
}
static PrintlnNode build() {
return PrintlnNodeGen.create();
}

View File

@ -9,6 +9,8 @@ import java.io.IOException;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.RuntimeError;
@BuiltinMethod(type = "IO", name = "readln", description = "Reads a line from standard in.")
@ -21,11 +23,11 @@ public abstract class ReadlnNode extends Node {
@Specialization
@TruffleBoundary
Object doRead(Object _this, @CachedContext(Language.class) Context ctx) {
Text doRead(Object _this, @CachedContext(Language.class) Context ctx) {
try {
return ctx.getInReader().readLine();
return Text.create(ctx.getInReader().readLine());
} catch (IOException e) {
return new RuntimeError("Empty input stream.");
throw new PanicException("Empty input stream", this);
}
}
}

View File

@ -1,14 +1,17 @@
package org.enso.interpreter.node.expression.builtin.system;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.io.TruffleProcessBuilder;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
import java.io.*;
@ -25,9 +28,9 @@ public abstract class CreateProcessNode extends Node {
abstract Object execute(
Object _this,
String command,
Text command,
Array arguments,
String input,
Text input,
boolean redirectIn,
boolean redirectOut,
boolean redirectErr);
@ -36,21 +39,25 @@ public abstract class CreateProcessNode extends Node {
@CompilerDirectives.TruffleBoundary
Object doCreate(
Object _this,
String command,
Text command,
Array arguments,
String input,
Text input,
boolean redirectIn,
boolean redirectOut,
boolean redirectErr,
@CachedContext(Language.class) Context ctx) {
String[] cmd = new String[(int) arguments.getItems().length + 1];
cmd[0] = command;
System.arraycopy(arguments.getItems(), 0, cmd, 1, (int) arguments.getItems().length);
@CachedContext(Language.class) Context ctx,
@Cached("build()") ToJavaStringNode toJavaStringNode) {
String[] cmd = new String[arguments.getItems().length + 1];
cmd[0] = toJavaStringNode.execute(command);
for (int i = 1; i <= arguments.getItems().length; i++) {
cmd[i] = toJavaStringNode.execute((Text) arguments.getItems()[i - 1]);
}
TruffleProcessBuilder pb = ctx.getEnvironment().newProcessBuilder(cmd);
try {
Process p = pb.start();
ByteArrayInputStream in = new ByteArrayInputStream(input.getBytes());
ByteArrayInputStream in =
new ByteArrayInputStream(toJavaStringNode.execute(input).getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
@ -106,8 +113,8 @@ public abstract class CreateProcessNode extends Node {
}
long exitCode = p.exitValue();
String returnOut = new String(out.toByteArray());
String returnErr = new String(err.toByteArray());
Text returnOut = Text.create(new String(out.toByteArray()));
Text returnErr = Text.create(new String(err.toByteArray()));
return ctx.getBuiltins()
.system()

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.node.expression.builtin.system;
import com.oracle.truffle.api.nodes.Node;
import org.apache.commons.lang3.SystemUtils;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.data.text.Text;
@BuiltinMethod(
type = "System",
@ -10,12 +11,12 @@ import org.enso.interpreter.dsl.BuiltinMethod;
description = "Get the type of operating system.")
public final class OsNode extends Node {
private final String LINUX = "linux";
private final String MACOS = "macos";
private final String WINDOWS = "windows";
private final String UNKNOWN = "unknown";
private final Text LINUX = Text.create("linux");
private final Text MACOS = Text.create("macos");
private final Text WINDOWS = Text.create("windows");
private final Text UNKNOWN = Text.create("unknown");
String execute(Object _this) {
Text execute(Object _this) {
if (SystemUtils.IS_OS_LINUX) return LINUX;
if (SystemUtils.IS_OS_MAC_OSX) return MACOS;
if (SystemUtils.IS_OS_WINDOWS) return WINDOWS;

View File

@ -1,13 +1,67 @@
package org.enso.interpreter.node.expression.builtin.text;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
@BuiltinMethod(type = "Any", name = "to_text", description = "Generic text conversion.")
public class AnyToTextNode extends Node {
public abstract class AnyToTextNode extends Node {
private static final int DISPATCH_CACHE = 3;
private @Child InteropLibrary displays =
InteropLibrary.getFactory().createDispatched(DISPATCH_CACHE);
private @Child InteropLibrary strings =
InteropLibrary.getFactory().createDispatched(DISPATCH_CACHE);
static AnyToTextNode build() {
return AnyToTextNodeGen.create();
}
abstract Text execute(Object _this);
@Specialization
Text doAtom(Atom at) {
if (at.getFields().length == 0) {
return Text.create(at.getConstructor().getName());
} else {
return doComplexAtom(at, displays, strings);
}
}
@Fallback
Text doOther(Object object) {
try {
return Text.create(strings.asString(displays.toDisplayString(object)));
} catch (UnsupportedMessageException e) {
return Text.create(object.toString());
}
}
@CompilerDirectives.TruffleBoundary
String execute(Object _this) {
return _this.toString();
private Text doComplexAtom(Atom atom, InteropLibrary displays, InteropLibrary strings) {
Text res = Text.create("(" + atom.getConstructor().getName() + " ");
try {
res = Text.create(res, strings.asString(displays.toDisplayString(atom.getFields()[0])));
} catch (UnsupportedMessageException e) {
res = Text.create(res, atom.getFields()[0].toString());
}
for (int i = 1; i < atom.getFields().length; i++) {
res = Text.create(res, " ");
try {
res = Text.create(res, strings.asString(displays.toDisplayString(atom.getFields()[i])));
} catch (UnsupportedMessageException e) {
res = Text.create(res, atom.getFields()[i].toString());
}
}
res = Text.create(res, ")");
return res;
}
}

View File

@ -2,10 +2,11 @@ package org.enso.interpreter.node.expression.builtin.text;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.data.text.Text;
@BuiltinMethod(type = "Text", name = "+", description = "Text concatenation.")
public class ConcatNode extends Node {
String execute(String _this, String that) {
return _this + that;
Text execute(Text _this, Text that) {
return Text.create(_this, that);
}
}

View File

@ -4,11 +4,12 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.builtin.LanguageEntitySerializer;
import org.enso.interpreter.runtime.data.text.Text;
@BuiltinMethod(type = "Any", name = "json_serialize", description = "Generic JSON serialization.")
public class JsonSerializeNode extends Node {
@CompilerDirectives.TruffleBoundary
String execute(Object _this) {
return LanguageEntitySerializer.serialize(_this);
Text execute(Object _this) {
return Text.create(LanguageEntitySerializer.serialize(_this));
}
}

View File

@ -0,0 +1,20 @@
package org.enso.interpreter.node.expression.builtin.text;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.runtime.data.text.Text;
@BuiltinMethod(
type = "Prim_Text_Helpers",
name = "optimize",
description = "Forces flattening of a text value, for testing or purposes.")
public class OptimizeNode extends Node {
private @Child ToJavaStringNode toJavaStringNode = ToJavaStringNode.build();
Text execute(Object _this, Text text) {
toJavaStringNode.execute(text);
return text;
}
}

View File

@ -1,11 +0,0 @@
package org.enso.interpreter.node.expression.builtin.text;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(type = "Text", name = "==", description = "Equality on text.")
public class TextEqualsNode extends Node {
boolean execute(String _this, String that) {
return _this.equals(that);
}
}

View File

@ -0,0 +1,91 @@
package org.enso.interpreter.node.expression.builtin.text.util;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.runtime.data.text.ConcatRope;
import org.enso.interpreter.runtime.data.text.Text;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.locks.Lock;
@NodeInfo(description = "Converts Enso Text to a Java String.")
public class ToJavaStringNode extends Node {
private static ToJavaStringNode UNCACHED = new ToJavaStringNode();
/**
* Returns the uncached version of this node.
*
* @return the uncached version of this node.
*/
public static ToJavaStringNode getUncached() {
return UNCACHED;
}
/**
* Creates a new instance of this node.
*
* @return a new instance of this node.
*/
public static ToJavaStringNode build() {
return new ToJavaStringNode();
}
/**
* Performs the conversion of Enso Text to a Java String.
*
* @param text the text to convert.
* @return the result of conversion.
*/
public String execute(Text text) {
if (text.isFlat()) {
return (String) text.getContents();
} else {
return inplaceFlatten(text);
}
}
/**
* Converts text to a Java String. For use outside of Truffle Nodes.
*
* @param text the text to convert.
* @return the result of conversion.
*/
@CompilerDirectives.TruffleBoundary
public static String inplaceFlatten(Text text) {
Lock lock = text.getLock();
lock.lock();
String result;
try {
if (text.isFlat()) {
result = (String) text.getContents();
} else {
result = doFlatten(text);
}
} finally {
lock.unlock();
}
return result;
}
private static String doFlatten(Text text) {
Deque<Object> workStack = new ArrayDeque<>();
StringBuilder bldr = new StringBuilder();
workStack.push(text.getContents());
while (!workStack.isEmpty()) {
Object item = workStack.pop();
if (item instanceof String) {
bldr.append((String) item);
} else {
ConcatRope rope = (ConcatRope) item;
workStack.push(rope.getRight());
workStack.push(rope.getLeft());
}
}
String res = bldr.toString();
text.setContents(res);
text.setFlat(true);
return res;
}
}

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.node.expression.debug;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.compiler.context.InlineContext;
@ -12,15 +13,18 @@ import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.argument.Thunk;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.Stateful;
/** Node running Enso expressions passed to it as strings. */
@NodeInfo(shortName = "Eval", description = "Evaluates code passed to it as string")
@ReportPolymorphism
public abstract class EvalNode extends BaseNode {
private final boolean shouldCaptureResultScope;
@ -54,7 +58,7 @@ public abstract class EvalNode extends BaseNode {
* @param expression the string containing expression to evaluate
* @return the result of evaluating {@code expression} in the {@code callerInfo} context
*/
public abstract Stateful execute(CallerInfo callerInfo, Object state, String expression);
public abstract Stateful execute(CallerInfo callerInfo, Object state, Text expression);
RootCallTarget parseExpression(LocalScope scope, ModuleScope moduleScope, String expression) {
LocalScope localScope = scope.createChild();
@ -93,11 +97,13 @@ public abstract class EvalNode extends BaseNode {
Stateful doCached(
CallerInfo callerInfo,
Object state,
String expression,
@Cached("expression") String cachedExpression,
Text expression,
@Cached("expression") Text cachedExpression,
@Cached("build()") ToJavaStringNode toJavaStringNode,
@Cached("toJavaStringNode.execute(expression)") String expressionStr,
@Cached("callerInfo") CallerInfo cachedCallerInfo,
@Cached(
"parseExpression(callerInfo.getLocalScope(), callerInfo.getModuleScope(), expression)")
"parseExpression(callerInfo.getLocalScope(), callerInfo.getModuleScope(), expressionStr)")
RootCallTarget cachedCallTarget,
@Cached("build()") ThunkExecutorNode thunkExecutorNode) {
Thunk thunk = new Thunk(cachedCallTarget, callerInfo.getFrame());
@ -108,10 +114,14 @@ public abstract class EvalNode extends BaseNode {
Stateful doUncached(
CallerInfo callerInfo,
Object state,
String expression,
@Cached("build()") ThunkExecutorNode thunkExecutorNode) {
Text expression,
@Cached("build()") ThunkExecutorNode thunkExecutorNode,
@Cached("build()") ToJavaStringNode toJavaStringNode) {
RootCallTarget callTarget =
parseExpression(callerInfo.getLocalScope(), callerInfo.getModuleScope(), expression);
parseExpression(
callerInfo.getLocalScope(),
callerInfo.getModuleScope(),
toJavaStringNode.execute(expression));
Thunk thunk = new Thunk(callTarget, callerInfo.getFrame());
return thunkExecutorNode.executeThunk(thunk, state, isTail());
}

View File

@ -3,14 +3,15 @@ package org.enso.interpreter.node.expression.literal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.data.text.Text;
/** Node representing a constant String value. */
@NodeInfo(shortName = "StringLiteral", description = "Constant string literal expression")
public class TextLiteralNode extends ExpressionNode {
private final String value;
private final Text value;
private TextLiteralNode(String value) {
this.value = value;
this.value = Text.create(value);
}
/**
@ -30,7 +31,7 @@ public class TextLiteralNode extends ExpressionNode {
* @return the string value this node was created with
*/
@Override
public Object executeGeneric(VirtualFrame frame) {
public Text executeGeneric(VirtualFrame frame) {
return value;
}
}

View File

@ -25,6 +25,7 @@ import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.type.Types;
@ -205,10 +206,7 @@ public class Module implements TruffleObject {
return scope;
}
/**
* Create scope if it does not exist.
*
*/
/** Create scope if it does not exist. */
public void ensureScopeExists() {
if (scope == null) {
scope = new ModuleScope(this);
@ -387,7 +385,7 @@ public class Module implements TruffleObject {
CallerInfo callerInfo = new CallerInfo(null, LocalScope.root(), scope);
Object state = context.getBuiltins().unit().newInstance();
return callOptimiserNode
.executeDispatch(eval, callerInfo, state, new Object[] {debug, expr})
.executeDispatch(eval, callerInfo, state, new Object[] {debug, Text.create(expr)})
.getValue();
}

View File

@ -5,18 +5,27 @@ import com.oracle.truffle.api.Truffle;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.debug.DebugBreakpointMethodGen;
import org.enso.interpreter.node.expression.builtin.debug.DebugEvalMethodGen;
import org.enso.interpreter.node.expression.builtin.error.*;
import org.enso.interpreter.node.expression.builtin.error.CatchErrorMethodGen;
import org.enso.interpreter.node.expression.builtin.error.CatchPanicMethodGen;
import org.enso.interpreter.node.expression.builtin.error.ThrowErrorMethodGen;
import org.enso.interpreter.node.expression.builtin.error.ThrowPanicMethodGen;
import org.enso.interpreter.node.expression.builtin.function.ApplicationOperatorMethodGen;
import org.enso.interpreter.node.expression.builtin.function.ExplicitCallFunctionMethodGen;
import org.enso.interpreter.node.expression.builtin.interop.generic.*;
import org.enso.interpreter.node.expression.builtin.interop.syntax.MethodDispatchNode;
import org.enso.interpreter.node.expression.builtin.interop.java.AddToClassPathMethodGen;
import org.enso.interpreter.node.expression.builtin.interop.java.LookupClassMethodGen;
import org.enso.interpreter.node.expression.builtin.interop.syntax.ConstructorDispatchNode;
import org.enso.interpreter.node.expression.builtin.io.*;
import org.enso.interpreter.node.expression.builtin.interop.syntax.MethodDispatchNode;
import org.enso.interpreter.node.expression.builtin.io.PrintErrMethodGen;
import org.enso.interpreter.node.expression.builtin.io.PrintlnMethodGen;
import org.enso.interpreter.node.expression.builtin.io.ReadlnMethodGen;
import org.enso.interpreter.node.expression.builtin.runtime.GCMethodGen;
import org.enso.interpreter.node.expression.builtin.runtime.NoInlineMethodGen;
import org.enso.interpreter.node.expression.builtin.state.*;
import org.enso.interpreter.node.expression.builtin.interop.java.*;
import org.enso.interpreter.node.expression.builtin.text.*;
import org.enso.interpreter.node.expression.builtin.state.GetStateMethodGen;
import org.enso.interpreter.node.expression.builtin.state.PutStateMethodGen;
import org.enso.interpreter.node.expression.builtin.state.RunStateMethodGen;
import org.enso.interpreter.node.expression.builtin.text.AnyToTextMethodGen;
import org.enso.interpreter.node.expression.builtin.text.JsonSerializeMethodGen;
import org.enso.interpreter.node.expression.builtin.thread.WithInterruptHandlerMethodGen;
import org.enso.interpreter.node.expression.builtin.unsafe.SetAtomFieldMethodGen;
import org.enso.interpreter.runtime.Context;
@ -47,16 +56,13 @@ public class Builtins {
private final AtomConstructor any;
private final Number number;
private final AtomConstructor function;
private final AtomConstructor text;
private final AtomConstructor debug;
private final Text text;
private final Error error;
private final Bool bool;
private final System system;
private final Array array;
private final RootCallTarget interopDispatchRoot;
private final FunctionSchema interopDispatchSchema;
private final Function newInstanceFunction;
private final Polyglot polyglot;
/**
* Creates an instance with builtin methods installed.
@ -74,10 +80,11 @@ public class Builtins {
error = new Error(language, scope);
array = new Array(language, scope);
function = new AtomConstructor("Function", scope).initializeFields();
text = new AtomConstructor("Text", scope).initializeFields();
text = new Text(language, scope);
debug = new AtomConstructor("Debug", scope).initializeFields();
system = new System(language, scope);
number = new Number(language, scope);
polyglot = new Polyglot(language, scope);
AtomConstructor nil = new AtomConstructor("Nil", scope).initializeFields();
AtomConstructor cons =
@ -98,7 +105,6 @@ public class Builtins {
scope.registerConstructor(unit);
scope.registerConstructor(any);
scope.registerConstructor(function);
scope.registerConstructor(text);
scope.registerConstructor(cons);
scope.registerConstructor(nil);
@ -114,8 +120,6 @@ public class Builtins {
scope.registerConstructor(unsafe);
createPolyglot(language);
scope.registerMethod(io, "println", PrintlnMethodGen.makeFunction(language));
scope.registerMethod(io, "print_err", PrintErrMethodGen.makeFunction(language));
scope.registerMethod(io, "readln", ReadlnMethodGen.makeFunction(language));
@ -138,8 +142,6 @@ public class Builtins {
scope.registerMethod(function, "call", ExplicitCallFunctionMethodGen.makeFunction(language));
scope.registerMethod(function, "<|", ApplicationOperatorMethodGen.makeFunction(language));
scope.registerMethod(text, "+", ConcatMethodGen.makeFunction(language));
scope.registerMethod(text, "==", TextEqualsMethodGen.makeFunction(language));
scope.registerMethod(any, "to_text", AnyToTextMethodGen.makeFunction(language));
scope.registerMethod(any, "json_serialize", JsonSerializeMethodGen.makeFunction(language));
@ -151,36 +153,9 @@ public class Builtins {
scope.registerMethod(unsafe, "set_atom_field", SetAtomFieldMethodGen.makeFunction(language));
interopDispatchRoot = Truffle.getRuntime().createCallTarget(MethodDispatchNode.build(language));
interopDispatchSchema =
new FunctionSchema(
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
FunctionSchema.CallerFrameAccess.NONE,
new ArgumentDefinition[] {
new ArgumentDefinition(1, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(2, "method_name", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(3, "arguments", ArgumentDefinition.ExecutionMode.EXECUTE)
},
new boolean[] {false, true, false},
new CallArgumentInfo[0]);
newInstanceFunction = ConstructorDispatchNode.makeFunction(language);
module.unsafeBuildIrStub();
}
private void createPolyglot(Language language) {
AtomConstructor polyglot = new AtomConstructor("Polyglot", scope).initializeFields();
scope.registerConstructor(polyglot);
scope.registerMethod(polyglot, "execute", ExecuteMethodGen.makeFunction(language));
scope.registerMethod(polyglot, "invoke", InvokeMethodGen.makeFunction(language));
scope.registerMethod(polyglot, "new", InstantiateMethodGen.makeFunction(language));
scope.registerMethod(polyglot, "get_member", GetMemberMethodGen.makeFunction(language));
scope.registerMethod(polyglot, "get_members", GetMembersMethodGen.makeFunction(language));
scope.registerMethod(polyglot, "get_array_size", GetArraySizeMethodGen.makeFunction(language));
scope.registerMethod(
polyglot, "get_array_element", GetArrayElementMethodGen.makeFunction(language));
}
/**
* Returns the {@code Unit} atom constructor.
*
@ -191,11 +166,11 @@ public class Builtins {
}
/**
* Returns the {@code Text} atom constructor.
* Returns the {@code Text} part of builtins.
*
* @return the {@code Text} atom constructor
* @return the {@code Text} part of builtins.
*/
public AtomConstructor text() {
public Text text() {
return text;
}
@ -255,6 +230,11 @@ public class Builtins {
return array;
}
/** @return the container for polyglot-related builtins. */
public Polyglot polyglot() {
return polyglot;
}
/**
* Returns the builtin module scope.
*
@ -267,20 +247,4 @@ public class Builtins {
public Module getModule() {
return module;
}
/**
* Builds a function dispatching to a polyglot method call.
*
* @param method the name and scope of the method this function will dispatch to.
* @return a function calling {@code method} with given arguments.
*/
public Function buildPolyglotMethodDispatch(UnresolvedSymbol method) {
Object[] preAppliedArr = new Object[] {null, method, null};
return new Function(interopDispatchRoot, null, interopDispatchSchema, preAppliedArr, null);
}
/** @return a function executing a constructor with given arguments. */
public Function getConstructorDispatch() {
return newInstanceFunction;
}
}

View File

@ -0,0 +1,107 @@
package org.enso.interpreter.runtime.builtin;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.interop.generic.*;
import org.enso.interpreter.node.expression.builtin.interop.syntax.*;
import org.enso.interpreter.node.expression.builtin.interop.syntax.GetArrayElementMethodGen;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.scope.ModuleScope;
/** A container class for all Polyglot-related stdlib builtins. */
public class Polyglot {
private final RootCallTarget interopDispatchRoot;
private final FunctionSchema interopDispatchSchema;
private final Function newInstanceFunction;
private final Function polyglotArrayLengthFunction;
private final Function polyglotArrayAtFunction;
private final Function polyglotToTextFunction;
/**
* Creates and registers all polyglot-related functions and types.
*
* @param language the current language instance.
* @param scope the builtin scope.
*/
public Polyglot(Language language, ModuleScope scope) {
// Note [Syntactic Functions]
interopDispatchRoot = Truffle.getRuntime().createCallTarget(MethodDispatchNode.build(language));
interopDispatchSchema =
new FunctionSchema(
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
FunctionSchema.CallerFrameAccess.NONE,
new ArgumentDefinition[] {
new ArgumentDefinition(1, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(2, "method_name", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(3, "arguments", ArgumentDefinition.ExecutionMode.EXECUTE)
},
new boolean[] {false, true, false},
new CallArgumentInfo[0]);
newInstanceFunction = ConstructorDispatchNode.makeFunction(language);
polyglotArrayAtFunction = GetArrayElementMethodGen.makeFunction(language);
polyglotArrayLengthFunction = ArrayLengthMethodGen.makeFunction(language);
polyglotToTextFunction = PolyglotToTextMethodGen.makeFunction(language);
createPolyglot(language, scope);
}
/* Note [Syntactic Functions]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~
* Certain functions in this module (ones that are not being directly registered in the scope)
* are not parts of the standard library. Instead, they are used by the method dispatch system
* to call into functionality of polyglot objects. As such, they are represented as Enso-level
* functions, but they are not directly accessible through the standard library.
*/
private void createPolyglot(Language language, ModuleScope scope) {
AtomConstructor polyglot = new AtomConstructor("Polyglot", scope).initializeFields();
scope.registerConstructor(polyglot);
scope.registerMethod(polyglot, "execute", ExecuteMethodGen.makeFunction(language));
scope.registerMethod(polyglot, "invoke", InvokeMethodGen.makeFunction(language));
scope.registerMethod(polyglot, "new", InstantiateMethodGen.makeFunction(language));
scope.registerMethod(polyglot, "get_member", GetMemberMethodGen.makeFunction(language));
scope.registerMethod(polyglot, "get_members", GetMembersMethodGen.makeFunction(language));
}
/*
* Builds a function dispatching to a polyglot method call.
*
* @param method the name and scope of the method this function will dispatch to.
* @return a function calling {@code method} with given arguments.
*/
public Function buildPolyglotMethodDispatch(UnresolvedSymbol method) {
Object[] preAppliedArr = new Object[] {null, method, null};
return new Function(interopDispatchRoot, null, interopDispatchSchema, preAppliedArr, null);
}
/** @return a function taking a polyglot array and returning its length. */
public Function getPolyglotArrayLengthFunction() {
return polyglotArrayLengthFunction;
}
/**
* @return a function taking a polyglot array and an index and accessing the item at the given
* index.
*/
public Function getPolyglotArrayAtFunction() {
return polyglotArrayAtFunction;
}
/** @return a function taking a polyglot object and displaying it as a human-readable string. */
public Function getPolyglotToTextFunction() {
return polyglotToTextFunction;
}
/** @return a function executing a constructor with given arguments. */
public Function getConstructorDispatch() {
return newInstanceFunction;
}
}

View File

@ -0,0 +1,34 @@
package org.enso.interpreter.runtime.builtin;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.text.ConcatMethodGen;
import org.enso.interpreter.node.expression.builtin.text.OptimizeMethodGen;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.scope.ModuleScope;
/** A container class for all Text-related stdlib builtins. */
public class Text {
private final AtomConstructor text;
/**
* Creates and registers all the text constructors and methods.
*
* @param language the current language instance.
* @param scope the scope to register constructors and methods in.
*/
public Text(Language language, ModuleScope scope) {
text = new AtomConstructor("Text", scope).initializeFields();
scope.registerConstructor(text);
AtomConstructor primTextHelpers =
new AtomConstructor("Prim_Text_Helper", scope).initializeFields();
scope.registerConstructor(primTextHelpers);
scope.registerMethod(text, "+", ConcatMethodGen.makeFunction(language));
scope.registerMethod(primTextHelpers, "optimize", OptimizeMethodGen.makeFunction(language));
}
/** @return the Text atom constructor. */
public AtomConstructor getText() {
return text;
}
}

View File

@ -9,6 +9,7 @@ import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import org.enso.interpreter.Constants;
import org.enso.interpreter.node.callable.MethodResolverNode;
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.scope.ModuleScope;
@ -99,11 +100,16 @@ public class UnresolvedSymbol implements TruffleObject {
UnresolvedSymbol symbol,
Object[] arguments,
@Cached MethodResolverNode methodResolverNode,
@Cached HostValueToEnsoNode hostValueToEnsoNode,
@CachedLibrary(limit = "BUILTIN_INTEROP_DISPATCH") InteropLibrary library)
throws ArityException, UnsupportedTypeException, UnsupportedMessageException {
Object[] args = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
args[i] = hostValueToEnsoNode.execute(arguments[i]);
}
if (arguments.length == 0) throw ArityException.create(1, 0);
Function function = methodResolverNode.execute(symbol, arguments[0]);
return library.execute(function, arguments);
Function function = methodResolverNode.execute(symbol, args[0]);
return library.execute(function, args);
}
}
}

View File

@ -2,12 +2,23 @@ package org.enso.interpreter.runtime.callable.atom;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.*;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.type.TypesGen;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/** A runtime representation of an Atom in Enso. */
@ -84,14 +95,65 @@ public class Atom implements TruffleObject {
return toString(false);
}
/**
* Displays a human-readable string representation of this atom.
*
* @param allowSideEffects whether or not to allow side effects in displaying the string
* @return a string representation of this atom
*/
@ExportMessage
public Object toDisplayString(boolean allowSideEffects) {
return this.toString();
public boolean hasMembers() {
return true;
}
@ExportMessage
@CompilerDirectives.TruffleBoundary
public Array getMembers(boolean includeInternal) {
Map<String, Function> members = constructor.getDefinitionScope().getMethods().get(constructor);
if (members == null) {
return new Array(0);
}
Object[] mems = members.keySet().toArray();
return new Array(mems);
}
@ExportMessage
@CompilerDirectives.TruffleBoundary
public boolean isMemberInvocable(String member) {
Map<String, ?> members = constructor.getDefinitionScope().getMethods().get(constructor);
return members != null && members.containsKey(member);
}
@ExportMessage
static class InvokeMember {
static UnresolvedSymbol buildSym(AtomConstructor cons, String name) {
return UnresolvedSymbol.build(name, cons.getDefinitionScope());
}
@Specialization(
guards = {"receiver.getConstructor() == cachedConstructor", "member.equals(cachedMember)"})
static Object doCached(
Atom receiver,
String member,
Object[] arguments,
@Cached(value = "receiver.getConstructor()", allowUncached = true)
AtomConstructor cachedConstructor,
@Cached(value = "member", allowUncached = true) String cachedMember,
@Cached(value = "buildSym(cachedConstructor, cachedMember)", allowUncached = true)
UnresolvedSymbol cachedSym,
@CachedLibrary("cachedSym") InteropLibrary symbols)
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
Object[] args = new Object[arguments.length + 1];
args[0] = receiver;
System.arraycopy(arguments, 0, args, 1, arguments.length);
return symbols.execute(cachedSym, args);
}
}
@ExportMessage
Text toDisplayString(boolean allowSideEffects, @CachedLibrary("this") InteropLibrary atoms) {
try {
return TypesGen.expectText(atoms.invokeMember(this, "to_text"));
} catch (UnsupportedMessageException
| ArityException
| UnknownIdentifierException
| UnsupportedTypeException
| UnexpectedResultException e) {
return Text.create(this.toString());
}
}
}

View File

@ -0,0 +1,28 @@
package org.enso.interpreter.runtime.data.text;
/** Represents a concatenation of two text values. */
public class ConcatRope {
private final Object left;
private final Object right;
/**
* Creates a new rope concatenating the arguments.
*
* @param left the left operand
* @param right the right operand
*/
public ConcatRope(Object left, Object right) {
this.left = left;
this.right = right;
}
/** @return the left operand of this concatenation. */
public Object getLeft() {
return left;
}
/** @return the right operand of this concatenation. */
public Object getRight() {
return right;
}
}

View File

@ -0,0 +1,128 @@
package org.enso.interpreter.runtime.data.text;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/** The main runtime type for Enso's Text. */
@ExportLibrary(InteropLibrary.class)
public class Text implements TruffleObject {
private volatile Object contents;
private volatile boolean isFlat;
private final Lock lock = new ReentrantLock();
private Text(String string) {
this.contents = string;
this.isFlat = true;
}
private Text(ConcatRope contents) {
this.contents = contents;
this.isFlat = false;
}
/**
* Wraps a string in an instance of Text.
*
* @param string the string to wrap.
* @return a Text corresponding to the original string.
*/
public static Text create(String string) {
return new Text(string);
}
/**
* Creates a new Text by concatenating two other texts.
*
* @param t1 the left operand.
* @param t2 the right operand.
* @return a Text representing concatenation of t1 and t2.
*/
public static Text create(Text t1, Text t2) {
return new Text(new ConcatRope(t1.contents, t2.contents));
}
/**
* Creates a new Text by concatenating a text and a string.
*
* @param t1 the left operand.
* @param t2 the right operand.
* @return a Text representing concatenation of t1 and t2.
*/
public static Text create(Text t1, String t2) {
return new Text(new ConcatRope(t1.contents, t2));
}
@ExportMessage
boolean isString() {
return true;
}
@ExportMessage
String asString(@Cached("build()") @Cached.Shared("strings") ToJavaStringNode toJavaStringNode) {
return toJavaStringNode.execute(this);
}
@ExportMessage
String toDisplayString(
boolean allowSideEffects,
@Cached("build()") @Cached.Shared("strings") ToJavaStringNode toJavaStringNode) {
String str = toJavaStringNode.execute(this);
// TODO This should be more extensible
String replaced =
str.replace("'", "\\'")
.replace("\n", "\\n")
.replace("\t", "\\t")
.replace("\u0007", "\\a")
.replace("\u0008", "\\b")
.replace("\u000c", "\\f")
.replace("\r", "\\r")
.replace("\u000B", "\\v")
.replace("\u001B", "\\e");
return "'" + replaced + "'";
}
/** @return true if this text wraps a string literal and does not require any optimization. */
public boolean isFlat() {
return isFlat;
}
/** @param flat the new value of the isFlat flag. */
public void setFlat(boolean flat) {
isFlat = flat;
}
/** @return the contents of this text. */
public Object getContents() {
return contents;
}
/**
* Sets the contents of this text.
*
* @param contents the new contents.
*/
public void setContents(Object contents) {
this.contents = contents;
}
/** @return the lock required for modification of this text. */
public Lock getLock() {
return lock;
}
@Override
public String toString() {
if (isFlat) {
return (String) this.contents;
} else {
return ToJavaStringNode.inplaceFlatten(this);
}
}
}

View File

@ -1,8 +1,14 @@
package org.enso.interpreter.runtime.error;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.interop.InteropLibrary;
/** A runtime object representing an arbitrary, user-created error. */
@ExportLibrary(InteropLibrary.class)
public class RuntimeError implements TruffleObject {
private final Object payload;
@ -33,4 +39,16 @@ public class RuntimeError implements TruffleObject {
public String toString() {
return "Error:" + getPayload().toString();
}
@ExportMessage
public String toDisplayString(
boolean allowSideEffects,
@CachedLibrary(limit = "3") InteropLibrary displays,
@CachedLibrary(limit = "3") InteropLibrary strings) {
try {
return "(Error: " + strings.asString(displays.toDisplayString(payload)) + ")";
} catch (UnsupportedMessageException e) {
return "Error";
}
}
}

View File

@ -11,6 +11,7 @@ import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.RuntimeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@ -28,7 +29,7 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger;
long.class,
boolean.class,
double.class,
String.class,
Text.class,
Function.class,
Atom.class,
AtomConstructor.class,
@ -109,7 +110,7 @@ public class Types {
return "Number";
} else if (TypesGen.isBoolean(value)) {
return "Boolean";
} else if (TypesGen.isString(value)) {
} else if (TypesGen.isText(value)) {
return "Text";
} else if (TypesGen.isFunction(value)) {
return "Function";

View File

@ -1,5 +1,7 @@
package org.enso.compiler.codegen
import java.nio.ByteBuffer
import cats.Foldable
import cats.implicits._
import org.enso.compiler.core.IR
@ -7,8 +9,17 @@ import org.enso.compiler.core.IR.Name.MethodReference
import org.enso.compiler.core.IR._
import org.enso.compiler.exception.UnhandledEntity
import org.enso.syntax.text.AST
import org.enso.syntax.text.Shape.{
SegmentEscape,
SegmentExpr,
SegmentPlain,
SegmentRawEscape
}
import org.enso.syntax.text.ast.text.Escape
import org.enso.syntax.text.ast.text.Escape.Unicode
import scala.annotation.tailrec
import scala.util.control.Breaks.{break, breakable}
/**
* This file contains the functionality that translates from the parser's
@ -480,16 +491,22 @@ object AstToIr {
.mkString("\n")
Literal.Text(fullString, getIdentifiedLocation(literal))
case AST.Literal.Text.Block.Fmt(_, _, _) =>
Error.Syntax(
literal,
Error.Syntax.UnsupportedSyntax("format strings")
)
case AST.Literal.Text.Line.Fmt(_) =>
Error.Syntax(
literal,
Error.Syntax.UnsupportedSyntax("format strings")
)
case AST.Literal.Text.Block.Fmt(lines, _, _) =>
val ls = lines.map(l => parseFmtSegments(literal, l.text))
val err = ls.collectFirst { case Left(e) => e }
err match {
case Some(err) => err
case None =>
val str = ls.collect { case Right(str) => str }.mkString("\n")
IR.Literal.Text(str, getIdentifiedLocation(literal))
}
case AST.Literal.Text.Line.Fmt(segments) =>
parseFmtSegments(literal, segments) match {
case Left(err) => err
case Right(str) =>
IR.Literal.Text(str, getIdentifiedLocation(literal))
}
case _ =>
throw new UnhandledEntity(literal.shape, "translateLiteral")
}
@ -497,6 +514,70 @@ object AstToIr {
}
}
private def parseFmtSegments(
literal: AST,
segments: Seq[AST.Literal.Text.Segment[AST]]
): Either[IR.Error, String] = {
val bldr = new StringBuilder
var err: Option[IR.Error] = None
breakable {
segments.foreach {
case SegmentEscape(code) =>
code match {
case Escape.Number(_) =>
err = Some(
Error.Syntax(
literal,
Error.Syntax.UnsupportedSyntax("escaped numbers")
)
)
break()
case unicode: Escape.Unicode =>
unicode match {
case Unicode.InvalidUnicode(unicode) =>
err = Some(
Error.Syntax(
literal,
Error.Syntax.InvalidEscapeSequence(unicode.repr)
)
)
break()
case Unicode._U16(digits) =>
val buffer = ByteBuffer.allocate(2)
buffer.putChar(
Integer.parseInt(digits, 16).asInstanceOf[Char]
)
val str = new String(buffer.array(), "UTF-16")
bldr.addAll(str)
case Unicode._U32(digits) =>
val buffer = ByteBuffer.allocate(4)
buffer.putInt(Integer.parseInt(digits, 16))
val str = new String(buffer.array(), "UTF-32")
bldr.addAll(str)
case Unicode._U21(digits) =>
val buffer = ByteBuffer.allocate(4)
buffer.putInt(Integer.parseInt(digits, 16))
val str = new String(buffer.array(), "UTF-32")
bldr.addAll(str)
}
case e: Escape.Character => bldr.addOne(e.code)
case e: Escape.Control => bldr.addAll(e.repr)
}
case SegmentPlain(text) => bldr.addAll(text)
case SegmentExpr(_) =>
err = Some(
Error.Syntax(
literal,
Error.Syntax.UnsupportedSyntax("interpolated expressions")
)
)
break()
case SegmentRawEscape(e) => bldr.addAll(e.repr)
}
}
err.map(Left(_)).getOrElse(Right(bldr.toString))
}
/**
* Translates a sequence literal into its [[IR]] counterpart.
* @param literal the literal to translate

View File

@ -65,6 +65,7 @@ import org.enso.interpreter.runtime.callable.function.{
FunctionSchema,
Function => RuntimeFunction
}
import org.enso.interpreter.runtime.data.text.Text
import org.enso.interpreter.runtime.error.DuplicateArgumentNameException
import org.enso.interpreter.runtime.scope.{LocalScope, ModuleScope}
import org.enso.interpreter.{Constants, Language}
@ -533,8 +534,10 @@ class IrToTruffle(
.error()
.syntaxError()
.newInstance(
Text.create(
"Type operators are not currently supported at runtime."
)
)
),
value.location
)
@ -573,7 +576,7 @@ class IrToTruffle(
val error = context.getBuiltins
.error()
.compileError()
.newInstance(message)
.newInstance(Text.create(message))
setLocation(ErrorNode.build(error), caseExpr.location)
}
@ -884,19 +887,40 @@ class IrToTruffle(
case Error.InvalidIR(_, _, _) =>
throw new CompilerError("Unexpected Invalid IR during codegen.")
case err: Error.Syntax =>
context.getBuiltins.error().syntaxError().newInstance(err.message)
context.getBuiltins
.error()
.syntaxError()
.newInstance(Text.create(err.message))
case err: Error.Redefined.Binding =>
context.getBuiltins.error().compileError().newInstance(err.message)
context.getBuiltins
.error()
.compileError()
.newInstance(Text.create(err.message))
case err: Error.Redefined.Method =>
context.getBuiltins.error().compileError().newInstance(err.message)
context.getBuiltins
.error()
.compileError()
.newInstance(Text.create(err.message))
case err: Error.Redefined.Atom =>
context.getBuiltins.error().compileError().newInstance(err.message)
context.getBuiltins
.error()
.compileError()
.newInstance(Text.create(err.message))
case err: Error.Redefined.ThisArg =>
context.getBuiltins.error().compileError().newInstance(err.message)
context.getBuiltins
.error()
.compileError()
.newInstance(Text.create(err.message))
case err: Error.Unexpected.TypeSignature =>
context.getBuiltins.error().compileError().newInstance(err.message)
context.getBuiltins
.error()
.compileError()
.newInstance(Text.create(err.message))
case err: Error.Resolution =>
context.getBuiltins.error().compileError().newInstance(err.message)
context.getBuiltins
.error()
.compileError()
.newInstance(Text.create(err.message))
case _: Error.Pattern =>
throw new CompilerError(
"Impossible here, should be handled in the pattern match."
@ -1064,8 +1088,10 @@ class IrToTruffle(
.error()
.syntaxError()
.newInstance(
Text.create(
"Typeset literals are not yet supported at runtime."
)
)
),
application.location
)

View File

@ -5570,6 +5570,10 @@ object IR {
def explanation: String
}
case class InvalidEscapeSequence(lit: String) extends Reason {
override def explanation: String = s"Invalid escape sequence $lit."
}
case object InvalidBaseInDecimalLiteral extends Reason {
override def explanation: String =
"Cannot change base of the fractional part of a number literal."

View File

@ -99,7 +99,7 @@ case object VectorLiterals extends IRPass {
case seq: IR.Application.Literal.Sequence =>
val trans = seq.mapExpressions(doExpression(_, vec))
IR.Application.Prefix(
vec,
vec.duplicate(),
List(IR.CallArgument.Specified(None, trans, None, None)),
false,
None

View File

@ -24,6 +24,7 @@ import org.enso.interpreter.instrument.{
Visualisation
}
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode.FunctionCall
import org.enso.interpreter.runtime.data.text.Text
import org.enso.interpreter.service.error.ServiceException
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.polyglot.runtime.Runtime.Api.ContextId
@ -301,6 +302,7 @@ trait ProgramExecutionSupport {
.leftMap(_.getMessage)
.flatMap {
case text: String => Right(text.getBytes("UTF-8"))
case text: Text => Right(text.toString.getBytes("UTF-8"))
case bytes: Array[Byte] => Right(bytes)
case other =>
Left(s"Cannot encode ${other.getClass} to byte array")

View File

@ -1,5 +1,6 @@
package org.enso.interpreter.runtime.builtin
import com.oracle.truffle.api.interop.InteropLibrary
import io.circe.Json
import org.enso.interpreter.runtime.callable.atom.{Atom, AtomConstructor}
@ -7,6 +8,7 @@ import org.enso.interpreter.runtime.callable.atom.{Atom, AtomConstructor}
* Helper for JSON-serializing runtime entities of the language.
*/
object LanguageEntitySerializer {
private val interopLibrary: InteropLibrary = InteropLibrary.getUncached()
/**
* Serializes a language entity into a JSON string. Returns null JSON for
@ -20,7 +22,6 @@ object LanguageEntitySerializer {
private def toJson(obj: Any): Json = obj match {
case l: Long => Json.fromLong(l)
case s: String => Json.fromString(s)
case cons: AtomConstructor =>
Json.obj("type" -> Json.fromString(cons.getName), "fields" -> Json.arr())
case atom: Atom =>
@ -28,6 +29,11 @@ object LanguageEntitySerializer {
"type" -> Json.fromString(atom.getConstructor.getName),
"fields" -> Json.arr(atom.getFields.map(toJson).toIndexedSeq: _*)
)
case _ => Json.Null
case _ =>
if (interopLibrary.isString(obj)) {
Json.fromString(interopLibrary.asString(obj))
} else {
Json.Null
}
}
}

View File

@ -29,7 +29,7 @@ class CompileDiagnosticsTest extends InterpreterTest {
| x = Panic.recover @
| x.catch to_text
|""".stripMargin
eval(code) shouldEqual "Syntax_Error Unrecognized token."
eval(code) shouldEqual "(Syntax_Error 'Unrecognized token.')"
}
"surface redefinition errors in the language" in {
@ -42,7 +42,7 @@ class CompileDiagnosticsTest extends InterpreterTest {
|
|main = Panic.recover here.foo . catch to_text
|""".stripMargin
eval(code) shouldEqual "Compile_Error Variable x is being redefined."
eval(code) shouldEqual "(Compile_Error 'Variable x is being redefined.')"
}
}
}

View File

@ -46,7 +46,7 @@ class ErrorsTest extends InterpreterTest {
|""".stripMargin
noException shouldBe thrownBy(eval(code))
consumeOut shouldEqual List("Error:MyError")
consumeOut shouldEqual List("(Error: MyError)")
}
"propagate through pattern matches" in {
@ -64,7 +64,7 @@ class ErrorsTest extends InterpreterTest {
| IO.println matched
|""".stripMargin
noException shouldBe thrownBy(eval(code))
consumeOut shouldEqual List("Error:MyError")
consumeOut shouldEqual List("(Error: MyError)")
}
"propagate through specialized pattern matches" in {
@ -83,7 +83,7 @@ class ErrorsTest extends InterpreterTest {
| IO.println (f brokenVal)
|""".stripMargin
noException shouldBe thrownBy(eval(code))
consumeOut shouldEqual List("1", "Error:MyError")
consumeOut shouldEqual List("1", "(Error: MyError)")
}
"be catchable by a user-provided special handling function" in {
@ -108,7 +108,7 @@ class ErrorsTest extends InterpreterTest {
| IO.println (unitErr.catch MyCons)
|""".stripMargin
eval(code)
consumeOut shouldEqual List("MyCons Unit")
consumeOut shouldEqual List("(MyCons Unit)")
}
"accept a method handle in catch function" in {
@ -126,7 +126,7 @@ class ErrorsTest extends InterpreterTest {
| IO.println(myErr.catch recover)
|""".stripMargin
eval(code)
consumeOut shouldEqual List("MyRecovered 20")
consumeOut shouldEqual List("(MyRecovered 20)")
}
"make the catch method an identity for non-error values" in {

View File

@ -46,7 +46,7 @@ class EvalTest extends InterpreterTest {
| IO.println (MyType x)
|""".stripMargin
eval(code)
consumeOut shouldEqual List("MyType 10")
consumeOut shouldEqual List("(MyType 10)")
}
"return a value usable in the caller scope" in {

View File

@ -74,7 +74,7 @@ class InteropTest extends InterpreterTest {
val code = "main = to_text"
val symbol = eval(code)
symbol.call(1) shouldEqual "1"
symbol.execute("Foo") shouldEqual "Foo"
symbol.execute("Foo") shouldEqual "'Foo'"
}
}
}

View File

@ -238,7 +238,7 @@ class NamedArgumentsTest extends InterpreterTest {
|main = Cons2 5
|""".stripMargin
eval(code).toString shouldEqual "Cons2 5 Nil2"
eval(code).toString shouldEqual "(Cons2 5 Nil2)"
}
"work with constructors" in {

View File

@ -42,10 +42,10 @@ class PolyglotTest extends InterpreterTest {
| class = Java.lookup_class "org.enso.example.TestClass"
| instance = Polyglot.new class Array.empty
| members = Polyglot.get_members instance
| IO.println (Polyglot.get_array_size members)
| IO.println (Polyglot.get_array_element members 0)
| IO.println (Polyglot.get_array_element members 1)
| IO.println (Polyglot.get_array_element members 2)
| IO.println members.length
| IO.println (members.at 0)
| IO.println (members.at 1)
| IO.println (members.at 2)
|""".stripMargin
eval(code)
val count :: methods = consumeOut

View File

@ -1,12 +1,12 @@
package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.{InterpreterTest, InterpreterContext}
import org.enso.interpreter.test.{InterpreterContext, InterpreterTest}
class TextTest extends InterpreterTest {
override def subject = "Text Library"
override def specify(
implicit interpreterContext: InterpreterContext
override def specify(implicit
interpreterContext: InterpreterContext
): Unit = {
"support text creation with single-line literals" in {
@ -40,12 +40,12 @@ class TextTest extends InterpreterTest {
|type My_Type a
|
|main =
| IO.println 5.to_text
| IO.println (My_Type (My_Type 10)).to_text
| IO.println "123".to_text
| IO.println 5
| IO.println (My_Type (My_Type 10))
| IO.println "123"
|""".stripMargin
eval(code)
consumeOut shouldEqual List("5", "My_Type (My_Type 10)", "123")
consumeOut shouldEqual List("5", "(My_Type (My_Type 10))", "123")
}
"support text creation with raw block literals" in {
@ -77,17 +77,14 @@ class TextTest extends InterpreterTest {
}
"support printing to standard error" in {
val errString = "\"My error string\""
val resultStr = errString.drop(1).dropRight(1)
val code =
s"""from Builtins import all
|
|main = IO.print_err $errString
|main = IO.print_err "My error string"
|""".stripMargin
eval(code)
consumeErr shouldEqual List(resultStr)
consumeErr shouldEqual List("My error string")
}
"support reading from standard input" in {

View File

@ -128,45 +128,47 @@ object Escape {
}
// Reference: https://en.wikipedia.org/wiki/String_literal
sealed trait Character extends Escape
sealed trait Character extends Escape {
def code: Char
}
object Character {
case object a extends Character {
val code: Int = '\u0007'
override val code: Char = '\u0007'
def name: String = toString
override val repr = name
}
case object b extends Character {
val code: Int = '\u0008'
override val code: Char = '\u0008'
def name: String = toString
override val repr = name
}
case object f extends Character {
val code: Int = '\u000C'
override val code: Char = '\u000C'
def name: String = toString
override val repr = name
}
case object n extends Character {
val code: Int = '\n'
override val code: Char = '\n'
def name: String = toString
override val repr = name
}
case object r extends Character {
val code: Int = '\r'
override val code: Char = '\r'
def name: String = toString
override val repr = name
}
case object t extends Character {
val code: Int = '\u0009'
override val code: Char = '\u0009'
def name: String = toString
override val repr = name
}
case object v extends Character {
val code: Int = '\u000B'
override val code: Char = '\u000B'
def name: String = toString
override val repr = name
}
case object e extends Character {
val code: Int = '\u001B'
override val code: Char = '\u001B'
def name: String = toString
override val repr = name
}
@ -229,32 +231,32 @@ object Escape {
override val repr = name
}
case object LF extends Control {
val code: Int = 0x0A
val code: Int = 0x0a
def name: String = toString
override val repr = name
}
case object VT extends Control {
val code: Int = 0x0B
val code: Int = 0x0b
def name: String = toString
override val repr = name
}
case object FF extends Control {
val code: Int = 0x0C
val code: Int = 0x0c
def name: String = toString
override val repr = name
}
case object CR extends Control {
val code: Int = 0x0D
val code: Int = 0x0d
def name: String = toString
override val repr = name
}
case object SO extends Control {
val code: Int = 0x0E
val code: Int = 0x0e
def name: String = toString
override val repr = name
}
case object SI extends Control {
val code: Int = 0x0F
val code: Int = 0x0f
def name: String = toString
override val repr = name
}
@ -309,37 +311,37 @@ object Escape {
override val repr = name
}
case object SUB extends Control {
val code: Int = 0x1A
val code: Int = 0x1a
def name: String = toString
override val repr = name
}
case object ESC extends Control {
val code: Int = 0x1B
val code: Int = 0x1b
def name: String = toString
override val repr = name
}
case object FS extends Control {
val code: Int = 0x1C
val code: Int = 0x1c
def name: String = toString
override val repr = name
}
case object GS extends Control {
val code: Int = 0x1D
val code: Int = 0x1d
def name: String = toString
override val repr = name
}
case object RS extends Control {
val code: Int = 0x1E
val code: Int = 0x1e
def name: String = toString
override val repr = name
}
case object US extends Control {
val code: Int = 0x1F
val code: Int = 0x1f
def name: String = toString
override val repr = name
}
case object DEL extends Control {
val code: Int = 0x7F
val code: Int = 0x7f
def name: String = toString
override val repr = name
}

View File

@ -12,7 +12,8 @@ import scala.annotation.tailrec
case class ParserDef() extends flexer.Parser[AST.Module] {
import ParserDef2._
final def unwrap[T](opt: Option[T]): T = opt match {
final def unwrap[T](opt: Option[T]): T =
opt match {
case None => throw new Error("Internal Error")
case Some(t) => t
}
@ -42,7 +43,8 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
//// Result ////
////////////////
override def getResult() = result.current.flatMap {
override def getResult() =
result.current.flatMap {
case AST.Module.any(mod) => Some(mod)
case _ => None
}
@ -52,13 +54,15 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
var current: Option[AST] = None
var stack: List[Option[AST]] = Nil
def push(): Unit = logger.trace {
def push(): Unit =
logger.trace {
logger.log(s"Pushed: $current")
stack +:= current
current = None
}
def pop(): Unit = logger.trace {
def pop(): Unit =
logger.trace {
current = stack.head
stack = stack.tail
logger.log(s"New result: ${current.map(_.show()).getOrElse("None")}")
@ -67,7 +71,8 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
def app(fn: String => AST): Unit =
app(fn(currentMatch))
def app(ast: AST): Unit = logger.trace {
def app(ast: AST): Unit =
logger.trace {
current = Some(current match {
case None => ast
case Some(r) => AST.App.Prefix(r, off.use(), ast)
@ -76,7 +81,8 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
def last(): Option[AST] = {
@tailrec
def go(ast: AST): AST = ast match {
def go(ast: AST): AST =
ast match {
case AST.App.Prefix.any(t) => go(t.arg)
case t => t
}
@ -92,18 +98,21 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
var current: Int = 0
var stack: List[Int] = Nil
def push(): Unit = logger.trace {
def push(): Unit =
logger.trace {
stack +:= current
current = 0
}
def pop(): Unit = logger.trace {
def pop(): Unit =
logger.trace {
current = stack.head
stack = stack.tail
logger.log(s"New offset: $current")
}
def use(): Int = logger.trace {
def use(): Int =
logger.trace {
val offset = current
current = 0
offset
@ -111,7 +120,8 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
def on(): Unit = on(0)
def on(shift: Int): Unit = logger.trace {
def on(shift: Int): Unit =
logger.trace {
val diff = currentMatch.length + shift
current += diff
logger.log(s"lastOffset + $diff = $current")
@ -126,33 +136,39 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
var current: Option[AST.Ident] = None
def on(cons: String => AST.Ident): Unit = logger.trace_ {
def on(cons: String => AST.Ident): Unit =
logger.trace_ {
on(cons(currentMatch))
}
def on(ast: AST.Ident): Unit = logger.trace {
def on(ast: AST.Ident): Unit =
logger.trace {
current = Some(ast)
state.begin(SFX_CHECK)
}
def submit(): Unit = logger.trace {
def submit(): Unit =
logger.trace {
result.app(unwrap(current))
current = None
}
def onErrSfx(): Unit = logger.trace {
def onErrSfx(): Unit =
logger.trace {
val ast = AST.Ident.InvalidSuffix(unwrap(current), currentMatch)
result.app(ast)
current = None
state.end()
}
def onNoErrSfx(): Unit = logger.trace {
def onNoErrSfx(): Unit =
logger.trace {
submit()
state.end()
}
def finalizer(): Unit = logger.trace {
def finalizer(): Unit =
logger.trace {
if (current.isDefined) submit()
}
@ -177,30 +193,36 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
//////////////////
final object opr {
def on(cons: String => AST.Ident): Unit = logger.trace {
def on(cons: String => AST.Ident): Unit =
logger.trace {
on(cons(currentMatch))
}
def onGrp(cons: String => AST.Ident): Unit = logger.trace {
def onGrp(cons: String => AST.Ident): Unit =
logger.trace {
ident.current = Some(cons(currentMatch))
ident.onNoErrSfx()
}
def onNoMod(cons: String => AST.Ident): Unit = logger.trace {
def onNoMod(cons: String => AST.Ident): Unit =
logger.trace {
onNoMod(cons(currentMatch))
}
def on(ast: AST.Ident): Unit = logger.trace {
def on(ast: AST.Ident): Unit =
logger.trace {
ident.current = Some(ast)
state.begin(MOD_CHECK)
}
def onNoMod(ast: AST.Ident): Unit = logger.trace {
def onNoMod(ast: AST.Ident): Unit =
logger.trace {
ident.current = Some(ast)
state.begin(SFX_CHECK)
}
def onMod(): Unit = logger.trace {
def onMod(): Unit =
logger.trace {
val opr = AST.Mod(unwrap(ident.current).asInstanceOf[AST.Opr].name)
ident.current = Some(opr)
}
@ -236,36 +258,42 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
var part1: String = ""
var part2: String = ""
def reset(): Unit = logger.trace {
def reset(): Unit =
logger.trace {
part1 = ""
part2 = ""
}
def submit(): Unit = logger.trace {
def submit(): Unit =
logger.trace {
val base = if (part1 == "") None else Some(part1)
result.app(AST.Number(base, part2))
reset()
}
def onDanglingBase(): Unit = logger.trace {
def onDanglingBase(): Unit =
logger.trace {
state.end()
result.app(AST.Number.DanglingBase(part2))
reset()
}
def onDecimal(): Unit = logger.trace {
def onDecimal(): Unit =
logger.trace {
part2 = currentMatch
state.begin(PHASE2)
}
def onExplicitBase(): Unit = logger.trace {
def onExplicitBase(): Unit =
logger.trace {
state.end()
part1 = part2
part2 = currentMatch.substring(1)
submit()
}
def onNoExplicitBase(): Unit = logger.trace {
def onNoExplicitBase(): Unit =
logger.trace {
state.end()
submit()
}
@ -301,27 +329,32 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
var stack: List[TextState] = Nil
var text: TextState = _
def push(): Unit = logger.trace {
def push(): Unit =
logger.trace {
stack +:= text
}
def pop(): Unit = logger.trace {
def pop(): Unit =
logger.trace {
text = stack.head
stack = stack.tail
}
def onInvalidQuote(): Unit = logger.trace {
def onInvalidQuote(): Unit =
logger.trace {
result.app(AST.Text.InvalidQuote(currentMatch))
}
def onInlineBlock(): Unit = logger.trace {
def onInlineBlock(): Unit =
logger.trace {
result.app(AST.Text.InlineBlock(currentMatch))
}
def finish(
raw: List[Line[Segment.Raw]] => AST,
fmt: List[Line[Segment.Fmt]] => AST
): Unit = logger.trace {
): Unit =
logger.trace {
submitLine()
val isFMT = state.current.parent.contains(FMT)
val body = text.lines.reverse
@ -333,30 +366,39 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
result.app(t)
}
def submit(segment: Segment.Fmt): Unit = logger.trace {
def submit(segment: Segment.Fmt): Unit =
logger.trace {
text.lineBuilder +:= segment
}
def submit(): Unit = logger.trace {
finish(t => AST.Text.Raw(t.head.text: _*), t => AST.Text(t.head.text: _*))
def submit(): Unit =
logger.trace {
finish(
t => AST.Text.Raw(t.head.text: _*),
t => AST.Text(t.head.text: _*)
)
}
def submitMissingQuote(): Unit = logger.trace {
def submitMissingQuote(): Unit =
logger.trace {
rewind()
submitUnclosed()
}
def submitInvalidQuote(): Unit = logger.trace {
def submitInvalidQuote(): Unit =
logger.trace {
submitUnclosed()
onInvalidQuote()
}
def submitUnclosed(): Unit = logger.trace {
def submitUnclosed(): Unit =
logger.trace {
val Text = AST.Text.Unclosed
finish(t => Text.Raw(t.head.text: _*), t => Text(t.head.text: _*))
}
def onEndOfBlock(): Unit = logger.trace {
def onEndOfBlock(): Unit =
logger.trace {
if (text.lineBuilder.isEmpty)
block.emptyLines = text.emptyLines ++ block.emptyLines
val (s, o) = (text.spaces, text.offset)
@ -365,13 +407,15 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
rewind()
}
def onBegin(grp: State): Unit = logger.trace {
def onBegin(grp: State): Unit =
logger.trace {
push()
state.begin(grp)
text = new TextState(0, 0, Nil, Nil, Nil)
}
def onBeginBlock(grp: State): Unit = logger.trace {
def onBeginBlock(grp: State): Unit =
logger.trace {
val offset = if (state.current == block.FIRSTCHAR) {
state.end()
block.current.offset
@ -392,7 +436,8 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
}
}
def submitPlainSegment(): Unit = logger.trace {
def submitPlainSegment(): Unit =
logger.trace {
text.lineBuilder = text.lineBuilder match {
case Shape.SegmentPlain(t) :: _ =>
Segment.Plain(t + currentMatch) :: text.lineBuilder.tail
@ -400,57 +445,75 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
}
}
def onEscape(code: Segment.Escape): Unit = logger.trace {
def onEscape(code: Segment.Escape): Unit =
logger.trace {
submit(Shape.SegmentEscape(code))
}
def onEscape(code: Segment.RawEscape): Unit = logger.trace {
def onEscape(code: Segment.RawEscape): Unit =
logger.trace {
submit(Shape.SegmentRawEscape(code))
}
def onEscapeU16(): Unit = logger.trace {
def onEscapeU21(): Unit =
logger.trace {
val code = currentMatch.drop(3).dropRight(1)
onEscape(Segment.Escape.Unicode.U21(code))
}
def onEscapeU16(): Unit =
logger.trace {
val code = currentMatch.drop(2)
onEscape(Segment.Escape.Unicode.U16(code))
}
def onEscapeU32(): Unit = logger.trace {
def onEscapeU32(): Unit =
logger.trace {
val code = currentMatch.drop(2)
onEscape(Segment.Escape.Unicode.U32(code))
}
def onEscapeInt(): Unit = logger.trace {
def onEscapeInt(): Unit =
logger.trace {
val int = currentMatch.drop(1).toInt
onEscape(Segment.Escape.Number(int))
}
def onEscapeInvalid(): Unit = logger.trace {
def onEscapeInvalid(): Unit =
logger.trace {
val chr = currentMatch.charAt(1)
onEscape(Segment.Escape.Invalid(chr))
}
def onEscapeUnfinished(): Unit = logger.trace {
def onEscapeUnfinished(): Unit =
logger.trace {
onEscape(Segment.Escape.Unfinished)
}
def onEscapeSlash(): Unit = logger.trace {
def onEscapeSlash(): Unit =
logger.trace {
onEscape(Segment.Escape.Slash)
}
def onEscapeQuote(): Unit = logger.trace {
def onEscapeQuote(): Unit =
logger.trace {
onEscape(Segment.Escape.Quote)
}
def onEscapeRawQuote(): Unit = logger.trace {
def onEscapeRawQuote(): Unit =
logger.trace {
onEscape(Segment.Escape.RawQuote)
}
def onInterpolateBegin(): Unit = logger.trace {
def onInterpolateBegin(): Unit =
logger.trace {
result.push()
off.push()
state.begin(INTERPOLATE)
}
def onInterpolateEnd(): Unit = logger.trace {
def onInterpolateEnd(): Unit =
logger.trace {
if (state.isInside(INTERPOLATE)) {
state.endTill(INTERPOLATE)
submit(Segment.Expr(result.current))
@ -462,8 +525,11 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
}
}
def submitLine(): Unit = logger.trace {
if (state.current == FMT_LINE || state.current == RAW_LINE || text.lineBuilder.nonEmpty) {
def submitLine(): Unit =
logger.trace {
if (
state.current == FMT_LINE || state.current == RAW_LINE || text.lineBuilder.nonEmpty
) {
val Line = Shape.TextBlockLine
text.lines +:= Line(text.emptyLines.reverse, text.lineBuilder.reverse)
text.lineBuilder = Nil
@ -471,12 +537,14 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
}
}
def onEndOfLine(): Unit = logger.trace {
def onEndOfLine(): Unit =
logger.trace {
state.begin(NEWLINE)
submitLine()
}
def onNewLine(): Unit = logger.trace {
def onNewLine(): Unit =
logger.trace {
state.end()
if (text.offset == OFFSET_OF_FIRST_LINE_FOUND)
text.offset = currentMatch.length
@ -488,11 +556,13 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
text.lineBuilder +:= Segment.Plain(" " * leadingSpaces)
}
def onEmptyLine(): Unit = logger.trace {
def onEmptyLine(): Unit =
logger.trace {
text.emptyLines :+= currentMatch.length - 1
}
def onEOFNewLine(): Unit = logger.trace {
def onEOFNewLine(): Unit =
logger.trace {
state.end()
onEndOfBlock()
state.begin(block.NEWLINE)
@ -504,9 +574,11 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
val fmtBlock = "'''" >> space.opt >> (eof | newline)
val rawBlock = "\"\"\"" >> space.opt >> (eof | newline)
val fmtChar = noneOf("'`\\\n")
val escapeChar = noneOf("'\\\"`\\\\\\n\\r{}")
val escape_int = "\\" >> num.decimal
val escape_u16 = "\\u" >> repeat(fmtChar, 0, 4)
val escape_u32 = "\\U" >> repeat(fmtChar, 0, 8)
val escape_u21 = "\\u{" >> repeat(escapeChar, 0, 8) >> "}"
val escape_u16 = "\\u" >> repeat(escapeChar, 0, 4)
val escape_u32 = "\\U" >> repeat(escapeChar, 0, 8)
val fmtSeg = fmtChar.many1
val rawSeg = noneOf("\"\n\\").many1
val fmtBSeg = noneOf("\n\\`").many1
@ -570,6 +642,7 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
text.FMT || s"\\$code" run s"text.onEscape($ctrl)"
}
text.FMT || text.escape_u21 || text.onEscapeU21()
text.FMT || text.escape_u16 || text.onEscapeU16()
text.FMT || text.escape_u32 || text.onEscapeU32()
text.FMT || text.escape_int || text.onEscapeInt()
@ -611,12 +684,14 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
emptyLines = Nil
}
def pop(): Unit = logger.trace {
def pop(): Unit =
logger.trace {
current = stack.head
stack = stack.tail
}
def build(): AST.Block = logger.trace {
def build(): AST.Block =
logger.trace {
submitLine()
AST.Block(
current.isOrphan,
@ -628,13 +703,15 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
)
}
def submit(): Unit = logger.trace {
def submit(): Unit =
logger.trace {
val block = build()
result.pop()
off.pop()
pop()
val block2: AST.Block = result.last() match {
case Some(AST.Opr.any(_)) => block.replaceType(AST.Block.Discontinuous)
case Some(AST.Opr.any(_)) =>
block.replaceType(AST.Block.Discontinuous)
case _ => block
}
result.app(block2)
@ -642,7 +719,8 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
logger.endGroup()
}
def submitModule(): Unit = logger.trace {
def submitModule(): Unit =
logger.trace {
val body = current.firstLine match {
case None => current.lines.reverse
case Some(line) => line.toOptional +: current.lines.reverse
@ -654,7 +732,8 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
logger.endGroup()
}
def submitLine(): Unit = logger.trace {
def submitLine(): Unit =
logger.trace {
result.current match {
case None =>
case Some(r) =>
@ -671,12 +750,14 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
result.current = None
}
def onEmptyLine(): Unit = logger.trace {
def onEmptyLine(): Unit =
logger.trace {
off.on(-1)
emptyLines +:= off.use()
}
def onModuleBegin(): Unit = logger.trace {
def onModuleBegin(): Unit =
logger.trace {
current.emptyLines = emptyLines.reverse
emptyLines = Nil
rewind()
@ -685,14 +766,16 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
state.begin(NEWLINE)
}
def onBegin(newIndent: Int): Unit = logger.trace {
def onBegin(newIndent: Int): Unit =
logger.trace {
val isOrphan = result.current.isEmpty
result.push()
push(newIndent, isOrphan)
logger.beginGroup()
}
def onEOFLine(): Unit = logger.trace {
def onEOFLine(): Unit =
logger.trace {
state.end()
submitLine()
off.on()
@ -701,12 +784,14 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
onEOF()
}
def onEndLine(): Unit = logger.trace {
def onEndLine(): Unit =
logger.trace {
off.push()
state.begin(NEWLINE)
}
def onNewLine(): Unit = logger.trace {
def onNewLine(): Unit =
logger.trace {
state.end()
off.on()
if (off.current == current.offset)
@ -718,7 +803,8 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
state.begin(FIRSTCHAR)
}
def onEnd(newIndent: Int): Unit = logger.trace {
def onEnd(newIndent: Int): Unit =
logger.trace {
while (newIndent < current.offset) submit()
if (newIndent > current.offset) {
logger.log("Block with invalid indentation")
@ -747,11 +833,13 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
/// Defaults ///
////////////////
final def onUnrecognized(): Unit = logger.trace {
final def onUnrecognized(): Unit =
logger.trace {
result.app(AST.Invalid.Unrecognized(_))
}
final def onEOF(): Unit = logger.trace {
final def onEOF(): Unit =
logger.trace {
ident.finalizer()
off.push()
block.submitLine()

106
std-bits/pom.xml Normal file
View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.enso</groupId>
<artifactId>base</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>base</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>67.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<outputDirectory>
${project.build.directory}/../../distribution/std-lib/Base/polyglot/java
</outputDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>2.0.0</version>
<executions>
<execution>
<id>add-third-party-licenses</id>
<phase>package</phase>
<goals>
<goal>download-licenses</goal>
</goals>
<configuration>
<licensesOutputDirectory>
${project.build.directory}/../../distribution/std-lib/Base/third-party-licenses
</licensesOutputDirectory>
</configuration>
</execution>
<execution>
<id>add-third-party-licenses-list</id>
<phase>package</phase>
<goals>
<goal>add-third-party</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/../../distribution/std-lib/Base/third-party-licenses
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>67.1</version>
<type>jar</type>
<overWrite>true</overWrite>
<destFileName>icu4j.jar</destFileName>
</artifactItem>
</artifactItems>
<outputDirectory>
${project.build.directory}/../../distribution/std-lib/Base/polyglot/java
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,87 @@
package org.enso.base;
import com.ibm.icu.text.Normalizer2;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;
/** Utils for standard library operations on Text. */
public class Text_Utils {
/**
* Creates a substring of the given string, indexing using the Java standard (UTF-16) indexing
* mechanism.
*
* @param string the string to substring
* @param from starting index
* @param to index one past the end of the desired substring
* @return a suitable substring
*/
public static String substring(String string, int from, int to) {
return string.substring(from, to);
}
/**
* Converts a string into an array of UTF-8 bytes.
*
* @param str the string to convert
* @return the UTF-8 representation of the string.
*/
public static byte[] get_bytes(String str) {
return str.getBytes(StandardCharsets.UTF_8);
}
/**
* Converts a string into an array of Unicode codepoints.
*
* @param str the string to convert
* @return the codepoints of the original string.
*/
public static int[] get_codepoints(String str) {
return str.codePoints().toArray();
}
/**
* Splits the string on each occurrence of {@code sep}, returning the resulting substrings in an
* array.
*
* @param str the string to split
* @param sep the separator string
* @return array of substrings of {@code str} contained between occurences of {@code sep}
*/
public static String[] split_at(String str, String sep) {
return str.split(Pattern.quote(sep));
}
/**
* Checks whether two strings are equal up to Unicode canonicalization.
*
* @param str1 the first string
* @param str2 the second string
* @return the result of comparison
*/
public static boolean equals(String str1, String str2) {
return Normalizer2.getNFDInstance()
.normalize(str1)
.equals(Normalizer2.getNFDInstance().normalize(str2));
}
/**
* Converts an array of codepoints into a string.
*
* @param codepoints the codepoints to convert
* @return the resulting string
*/
public static String from_codepoints(int[] codepoints) {
return new String(codepoints, 0, codepoints.length);
}
/**
* Converts an array of UTF-8 bytes into a string.
*
* @param bytes the bytes to convert
* @return the resulting string
*/
public static String from_utf_8(byte[] bytes) {
return new String(bytes, StandardCharsets.UTF_8);
}
}

View File

@ -19,6 +19,12 @@ sum_tco_decimal = sum_to ->
res = summator 0.0 0.0
res
sum_tco_eval = sumTo ->
summator = acc -> current ->
if current == 0 then acc else Debug.eval "summator (acc + current) (current - 1)"
res = summator 0 sumTo
res
sum_tco_java = sum_to ->
summator = acc -> current ->
@ -49,11 +55,13 @@ sum_state = sum_to ->
main =
hundred_mil = 100000000
IO.println "Measuring Sum TCO Decimal"
Bench_Utils.measure (here.sum_tco_decimal hundred_mil) "sum_tco" 100 10
Bench_Utils.measure (here.sum_tco_decimal hundred_mil) "sum_tco_float" 100 10
IO.println "Measuring SumTCO"
Bench_Utils.measure (here.sum_tco hundred_mil) "sum_tco" 100 10
IO.println "Measuring SumTCO Java"
Bench_Utils.measure (here.sum_tco_java hundred_mil) "sum_tco" 100 10
Bench_Utils.measure (here.sum_tco_java hundred_mil) "sum_tco_java" 100 10
IO.println "Measuring SumTCO Eval"
Bench_Utils.measure (here.sum_tco_eval hundred_mil) "sum_tco_eval" 100 10
IO.println "Measuring State"
Bench_Utils.measure (here.sum_state hundred_mil) "sum_state" 100 10
IO.println "Measuring Co-State"

View File

@ -0,0 +1,21 @@
from Base import all
from Builtins import Prim_Text_Helper
import Base.Bench_Utils
polyglot java import java.lang.StringBuilder
build_long n =
res = 1.upto n . fold "" acc-> n-> acc + n.to_text
Prim_Text_Helper.optimize res
res
build_long_bldr n =
bldr = new StringBuilder [].to_array
1.upto n . each n-> bldr.append [n]
res = bldr.toString []
res
main =
Bench_Utils.measure (here.build_long_bldr 1000000) "string append bldr" 100 10
Bench_Utils.measure (here.build_long 1000000) "string append" 100 10
IO.println "Bye"

View File

@ -4,11 +4,22 @@ import Base.Test
polyglot java import java.lang.Long
polyglot java import java.lang.Integer
polyglot java import java.lang.Float
polyglot java import java.lang.String
polyglot java import java.util.ArrayList
spec = describe "Java FFI" <|
it "should call methods imported from Java" <|
Long.sum [1, 2] . should_equal 3
## TODO
https://github.com/enso-org/enso/issues/1163
it "should call constructors imported from Java" pending=True <|
list = ArrayList.new []
list.add 432
list.get [0] . should_equal 432
it "should auto-convert numeric types across the polyglot boundary" <|
(Float.valueOf ["123.3"] + 5).should_equal 128.3 epsilon=0.0001
(Integer.sum [1, 2] + 3) . should_equal 6
it "should auto-convert strings across the polyglot boundary" <|
(String.format ["%s bar %s", "baz", "quux"] + " foo").should_equal "baz bar quux foo"

View File

@ -8,6 +8,7 @@ import Test.Process_Spec
import Test.Java_Interop.Spec as Java_Spec
import Test.Vector.Spec as Vector_Spec
import Test.Numbers.Spec as Numbers_Spec
import Test.Text.Spec as Text_Spec
main = Test.Suite.runMain <|
List_Spec.spec
@ -19,3 +20,4 @@ main = Test.Suite.runMain <|
Java_Spec.spec
Vector_Spec.spec
Numbers_Spec.spec
Text_Spec.spec

View File

@ -0,0 +1,42 @@
from Base import all
import Base.Test
type Auto a
type Manual b
Manual.to_text = "[[[MyREP " + this.b.to_text + "]]]"
spec = describe "Text" <|
kshi = '\u0915\u094D\u0937\u093F'
kshi_utf_8 = [-32, -92, -107, -32, -91, -115, -32, -92, -73, -32, -92, -65]
facepalm = '\u{1F926}\u{1F3FC}\u200D\u2642\uFE0F'
facepalm_codes = [129318, 127996, 8205, 9794, 65039]
accent_1 = '\u00E9'
accent_2 = '\u0065\u{301}'
it "should compare strings using utf normalization" <|
"abc".equals "def" . should_be_false
accent_1 . should_equal accent_2
it "should split the text into grapheme clusters" <|
str = kshi + facepalm + accent_1 + accent_2
str.characters . should_equal [kshi, facepalm, accent_1, accent_2]
it "should split the text on arbitrary sequence" <|
"foo, bar, baz" . split_at ", " . should_equal ["foo", "bar", "baz"]
it "should dump utf-8 bytes to a vector" <|
kshi.utf_8.should_equal kshi_utf_8
it "should convert an array of bytes to text" <|
Text.from_utf_8 kshi_utf_8 . should_equal kshi
it "should dump utf codepoints to a vector" <|
facepalm.codepoints.should_equal facepalm_codes
it "should convert an array of codepoints to text" <|
Text.from_codepoints facepalm_codes . should_equal facepalm
it "should convert any type to text automatically and using provided methods" <|
t = Auto (Manual 123) . to_text
t.should_equal "(Auto [[[MyREP 123]]])"
it "should escape special characters when debug-printing text" <|
text_1 = '''
foo
bar\r\tbaz
text_1.to_text.should_equal "'foo\\nbar\\r\\tbaz'"
text_2 = '\n\t\a\b\f\r\v\e\''
text_2.to_text.should_equal "'\\n\\t\\a\\b\\f\\r\\v\\e\\''"

View File

@ -19,4 +19,8 @@ spec = describe "Vectors" <|
mapped = vec.map x-> x * x
vec.to_text.should_equal "[1, 2, 3, 4]"
mapped.to_text.should_equal "[1, 4, 9, 16]"
it "should define equality" <|
[1,2,3].equals [1,2] . should_be_false
[1,2,3].equals [1,2,3] . should_be_true
[1,2,3].equals [3,4,5] . should_be_false