Skip to content

Commit

Permalink
Rename source files and organize imports and soundness exports
Browse files Browse the repository at this point in the history
  • Loading branch information
propensive committed Jun 6, 2024
1 parent 81e9993 commit 2bc97ce
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 34 deletions.
Binary file added src/.DS_Store
Binary file not shown.
22 changes: 22 additions & 0 deletions src/core/contextual.Insertion.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
Contextual, version [unreleased]. Copyright 2024 Jon Pretty, Propensive OÜ.
The primary distribution site is: https://propensive.com/
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions
and limitations under the License.
*/

package contextual

import language.experimental.captureChecking

trait Insertion[InputType, -ValueType]:
def embed(value: ValueType): InputType
27 changes: 27 additions & 0 deletions src/core/contextual.InterpolationError.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Contextual, version [unreleased]. Copyright 2024 Jon Pretty, Propensive OÜ.
The primary distribution site is: https://propensive.com/
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions
and limitations under the License.
*/

package contextual

import language.experimental.captureChecking

import scala.compiletime.*

import fulminate.*
import vacuous.*

case class InterpolationError(error: Message, offset: Optional[Int] = Unset, length: Optional[Int] = Unset)
extends Error(msg"$error at ${offset.or(-1)} - ${length.or(-1)}")
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package contextual

import language.experimental.captureChecking

import scala.quoted.*
import scala.compiletime.*

Expand All @@ -24,23 +26,6 @@ import rudiments.*
import vacuous.*
import anticipation.*

import language.experimental.captureChecking

case class InterpolationError(error: Message, offset: Optional[Int] = Unset, length: Optional[Int] = Unset)
extends Error(msg"$error at ${offset.or(-1)} - ${length.or(-1)}")

trait Verifier[ResultType]
extends Interpolator[Nothing, Optional[ResultType], ResultType]:
def verify(text: Text): ResultType
protected def initial: Optional[ResultType] = Unset
protected def parse(state: Optional[ResultType], next: Text): Optional[ResultType] = verify(next)
protected def skip(state: Optional[ResultType]): Optional[ResultType] = state
protected def insert(state: Optional[ResultType], value: Nothing): Optional[ResultType] = state
protected def complete(value: Optional[ResultType]): ResultType = value.option.get

def expand(context: Expr[StringContext])(using Quotes, Type[ResultType])(using thisType: Type[this.type])
: Expr[ResultType] = expand(context, '{Nil})(using thisType)

trait Interpolator[InputType, StateType, ResultType]:
given CanThrow[InterpolationError] = ###
given Realm = realm"contextual"
Expand All @@ -51,7 +36,7 @@ trait Interpolator[InputType, StateType, ResultType]:
protected def substitute(state: StateType, value: Text): StateType = parse(state, value)
protected def insert(state: StateType, value: InputType): StateType
protected def complete(value: StateType): ResultType

case class PositionalError(positionalMessage: Message, start: Int, end: Int)
extends Error(msg"error $positionalMessage at position $start")

Expand All @@ -60,7 +45,7 @@ trait Interpolator[InputType, StateType, ResultType]:
: Expr[ResultType] =

expansion(context, seq)(1)

def expansion
(context: Expr[StringContext], seq: Expr[Seq[Any]])
(using thisType: Type[this.type])
Expand Down Expand Up @@ -89,7 +74,7 @@ trait Interpolator[InputType, StateType, ResultType]:
case '{$head: headType} +: tail =>
def notFound: Nothing =
val typeName: String = TypeRepr.of[headType].widen.show

abandon(msg"can't substitute ${Text(typeName)} into this interpolated string", head.asTerm.pos)

val (newState, typeclass) = Expr.summon[Insertion[InputType, headType]].fold(notFound): insertion =>
Expand All @@ -98,36 +83,36 @@ trait Interpolator[InputType, StateType, ResultType]:
val substitution: String = (TypeRepr.of[subType].asMatchable: @unchecked) match
case ConstantType(StringConstant(string)) =>
string

(rethrow(parse(rethrow(substitute(state, Text(substitution)), expr.asTerm.pos.start, expr.asTerm.pos.end),
Text(parts.head)), positions.head.start, positions.head.end), typeclass)

case '{$typeclass: eType} =>
(rethrow(parse(rethrow(skip(state), expr.asTerm.pos.start, expr.asTerm.pos.end), Text(parts.head)),
positions.head.start, positions.head.end), typeclass)

val next = '{$target.parse($target.insert($expr, $typeclass.embed($head)),
Text(${Expr(parts.head)}))}

recur(tail, parts.tail, positions.tail, newState, next)

case _ =>
rethrow(complete(state), Position.ofMacroExpansion.start, Position.ofMacroExpansion.end)
(state, '{$target.complete($expr)})

val exprs: Seq[Expr[Any]] = seq match
case Varargs(exprs) => exprs
case _ => Nil

val parts = context.value.getOrElse:
abandon(msg"the StringContext extension method parameter does not appear to be inline")
.parts

val positions: Seq[Position] = (context: @unchecked) match
case '{(${sc}: StringContext.type).apply(($parts: Seq[String])*)} =>
(parts: @unchecked) match
case Varargs(stringExprs) => stringExprs.to(List).map(_.asTerm.pos)

try recur(exprs, parts.tail, positions.tail, rethrow(parse(initial, Text(parts.head)),
positions.head.start, positions.head.end), '{$target.parse($target.initial, Text(${Expr(parts.head)}))})
catch
Expand All @@ -136,9 +121,3 @@ trait Interpolator[InputType, StateType, ResultType]:

case err: InterpolationError => err match
case InterpolationError(message, _, _) => abandon(message, Position.ofMacroExpansion)

trait Insertion[InputType, -ValueType]:
def embed(value: ValueType): InputType

trait Substitution[InputType, -ValueType, SubstitutionType <: Label]
extends Insertion[InputType, ValueType]
24 changes: 24 additions & 0 deletions src/core/contextual.Substitution.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
Contextual, version [unreleased]. Copyright 2024 Jon Pretty, Propensive OÜ.
The primary distribution site is: https://propensive.com/
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions
and limitations under the License.
*/

package contextual

import language.experimental.captureChecking

import rudiments.*

trait Substitution[InputType, -ValueType, SubstitutionType <: Label]
extends Insertion[InputType, ValueType]
36 changes: 36 additions & 0 deletions src/core/contextual.Verifier.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Contextual, version [unreleased]. Copyright 2024 Jon Pretty, Propensive OÜ.
The primary distribution site is: https://propensive.com/
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions
and limitations under the License.
*/

package contextual

import language.experimental.captureChecking

import scala.quoted.*

import vacuous.*
import anticipation.*

trait Verifier[ResultType]
extends Interpolator[Nothing, Optional[ResultType], ResultType]:
def verify(text: Text): ResultType
protected def initial: Optional[ResultType] = Unset
protected def parse(state: Optional[ResultType], next: Text): Optional[ResultType] = verify(next)
protected def skip(state: Optional[ResultType]): Optional[ResultType] = state
protected def insert(state: Optional[ResultType], value: Nothing): Optional[ResultType] = state
protected def complete(value: Optional[ResultType]): ResultType = value.option.get

def expand(context: Expr[StringContext])(using Quotes, Type[ResultType])(using thisType: Type[this.type])
: Expr[ResultType] = expand(context, '{Nil})(using thisType)
19 changes: 19 additions & 0 deletions src/core/soundness+contextual-core.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
Contextual, version [unreleased]. Copyright 2024 Jon Pretty, Propensive OÜ.
The primary distribution site is: https://propensive.com/
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions
and limitations under the License.
*/

package soundness

export contextual.{Insertion, InterpolationError, Interpolator, Verifier, Substitution}

0 comments on commit 2bc97ce

Please sign in to comment.