Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,10 @@ object CheckExprTypes extends UseAnalyzer {
for {
a <- super.exprBinopNode(a, node, e)
t <- a.commonType(e.e1.id, e.e2.id, loc)
_ <- convertToNumeric(loc, t)
_ <- e.op match {
case Ast.Binop.Add => convertToNumericOrString(loc, t)
case _ => convertToNumeric(loc, t)
}
} yield a.assignType(node -> t)
}

Expand Down Expand Up @@ -481,5 +484,13 @@ object CheckExprTypes extends UseAnalyzer {
Left(error)
}
}


private def convertToNumericOrString(loc: Location, t: Type): Result.Result[Type] = {
t match {
case _: Type.String => Right(t)
case _ if t.isNumeric => Right(t)
case _ if t.isConvertibleTo(Type.Integer) => Right(Type.Integer)
case _ => Left(SemanticError.InvalidType(loc, s"cannot convert $t to a numeric or string type"))
}
}
}
5 changes: 5 additions & 0 deletions compiler/lib/src/main/scala/analysis/Semantics/Value.scala
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ object Value {

override def toString = "\"" + value.toString + "\""

override private[analysis] def binop(op: Binop)(v: Value) = v match {
case String(value2) => Some(String(value + value2))
case _ => None
}

}

/** Anonymous array values */
Expand Down
1 change: 1 addition & 0 deletions compiler/lib/src/test/scala/semantics/ValueSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class ValueSpec extends AnyWordSpec {
(createI32(1), array, None),
(createI32(1), anonStruct, None),
(createI32(1), struct, None),
(String("abc"), String("def"), Some(String("abcdef")))
)
triples.foreach {
triple => s"sum ${triple._1} and ${triple._2} to ${triple._3}" in
Expand Down
1 change: 1 addition & 0 deletions compiler/tools/fpp-check/test/expr/string_concat_error.fpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
constant a = { x = 1 } + { x = 2 }
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fpp-check
[ local path prefix ]/compiler/tools/fpp-check/test/expr/string_concat_error.fpp:1.24
constant a = { x = 1 } + { x = 2 }
^
error: cannot convert { x: Integer } to a numeric or string type
1 change: 1 addition & 0 deletions compiler/tools/fpp-check/test/expr/string_concat_ok.fpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
constant s = "abc" + "cdef"
Empty file.
2 changes: 2 additions & 0 deletions compiler/tools/fpp-check/test/expr/tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ sizeof_not_displayable
sizeof_ok
sizeof_string_fw_store_type_not_defined
struct_duplicate
string_concat_ok
string_concat_error
"
127 changes: 94 additions & 33 deletions docs/fpp-spec.html

Large diffs are not rendered by default.

68 changes: 47 additions & 21 deletions docs/fpp-users-guide.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.26">
<meta name="generator" content="Asciidoctor 2.0.16">
<title>The F Prime Prime (FPP) User&#8217;s Guide, Unreleased, after v3.1.0</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
Expand Down Expand Up @@ -84,10 +84,10 @@
ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ul.square{list-style-type:square}
ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
Expand Down Expand Up @@ -116,6 +116,7 @@
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre>code{display:block}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
Expand All @@ -139,7 +140,7 @@
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child{border-bottom:1px solid #dddddf;padding-bottom:8px}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
Expand All @@ -161,7 +162,6 @@
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
Expand Down Expand Up @@ -193,8 +193,7 @@
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details{margin-left:1.25rem}
details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent}
details>summary::-webkit-details-marker{display:none}
details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;-webkit-tap-highlight-color:transparent}
details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}
details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}
details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}
Expand All @@ -208,17 +207,19 @@
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:first-child,.sidebarblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child,.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock pre>code{display:block}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
Expand All @@ -234,8 +235,9 @@
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
pre.pygments span.linenos{display:inline-block;margin-right:.75em}
table.linenotable td.linenos{border-right:1px solid;opacity:.35;padding-right:.5em}
pre.pygments .lineno{border-right:1px solid;opacity:.35;display:inline-block;margin-right:.75em}
pre.pygments .lineno::before{content:"";margin-right:-.125em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
Expand Down Expand Up @@ -272,7 +274,7 @@
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7}
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
Expand All @@ -288,11 +290,10 @@
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
li>p:empty:only-child::before{content:"";display:inline-block}
ul.checklist>li>p:first-child{margin-left:-1em}
ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}
Expand Down Expand Up @@ -328,13 +329,15 @@
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active,#footnotes .footnote a:first-of-type:active{text-decoration:underline}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
Expand Down Expand Up @@ -391,7 +394,7 @@
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{font-size:1.0625rem}
p,blockquote,dt,td.content,span.alt,summary{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
Expand Down Expand Up @@ -462,7 +465,8 @@ <h1>The F Prime Prime (FPP) User&#8217;s Guide, Unreleased, after v3.1.0</h1>
<li><a href="#Defining-Constants_Expressions_Struct-Members">3.3.6. Struct Members</a></li>
<li><a href="#Defining-Constants_Expressions_Name-Expressions">3.3.7. Name Expressions</a></li>
<li><a href="#Defining-Constants_Expressions_Value-Arithmetic-Expressions">3.3.8. Value Arithmetic Expressions</a></li>
<li><a href="#Defining-Constants_Expressions_Compound-Expressions">3.3.9. Compound Expressions</a></li>
<li><a href="#Defining-Constants_Expressions_String-Concatenation">3.3.9. String Concatenation</a></li>
<li><a href="#Defining-Constants_Expressions_Compound-Expressions">3.3.10. Compound Expressions</a></li>
</ul>
</li>
<li><a href="#Defining-Constants_Multiple-Definitions-and-Element-Sequences">3.4. Multiple Definitions and Element Sequences</a></li>
Expand Down Expand Up @@ -1648,9 +1652,31 @@ <h4 id="Defining-Constants_Expressions_Value-Arithmetic-Expressions">3.3.8. Valu
</li>
</ul>
</div>
<div class="paragraph">
<p>For the use of <code>+</code> with string operands, see
<a href="#Defining-Constants_Expressions_String-Concatenation">String Concatenation</a> below.</p>
</div>
</div>
<div class="sect3">
<h4 id="Defining-Constants_Expressions_String-Concatenation">3.3.9. String Concatenation</h4>
<div class="paragraph">
<p>You can use the <code>+</code> operator to concatenate two string values.
Both operands must have string type. The result is a string formed
by appending the second operand to the first. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="fpp">constant c = "a" + "b" + "c" # value is "abc"
constant a = "hello " + c + "!" # value is "hello abc!"</code></pre>
</div>
</div>
<div class="paragraph">
<p>Since <code>+</code> is left-associative, <code>"a" + "b" + "c"</code> is evaluated as
<code>("a" + "b") + "c"</code>.</p>
</div>
</div>
<div class="sect3">
<h4 id="Defining-Constants_Expressions_Compound-Expressions">3.3.9. Compound Expressions</h4>
<h4 id="Defining-Constants_Expressions_Compound-Expressions">3.3.10. Compound Expressions</h4>
<div class="paragraph">
<p>Wherever you can write a value inside an expression, you can write
a more complex expression there, so long as the types work out.
Expand Down Expand Up @@ -16602,7 +16628,7 @@ <h3 id="Writing-C-Plus-Plus-Implementations_Serialization-of-FPP-Values">17.4. S
</div>
<div id="footer">
<div id="footer-text">
Last updated 2026-03-18 11:18:20 -0700
Last updated 2026-03-26 00:39:18 +0530
</div>
</div>
<script src="code-prettify/run_prettify.js"></script>
Expand All @@ -16629,6 +16655,6 @@ <h3 id="Writing-C-Plus-Plus-Implementations_Serialization-of-FPP-Values">17.4. S
})
})
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.9/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.9/MathJax.js?config=TeX-MML-AM_HTMLorMML"></script>
</body>
</html>
Loading