Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
1ea2f44
claude: gen problem statements
XomaDev Mar 10, 2026
d8cb40d
claude: +200 PROBLEM2.md
XomaDev Mar 10, 2026
46b1a67
claude: more problem statements
XomaDev Mar 11, 2026
86385f7
dev: runtime for falcon
XomaDev Mar 11, 2026
2898643
dev: write test cases
XomaDev Mar 11, 2026
712d6ca
claude: convert to JSONs
XomaDev Mar 11, 2026
db4c49f
fix: variables result bug
XomaDev Mar 14, 2026
87570bb
dev: initial runtime refactor
XomaDev Mar 21, 2026
4f839cf
dev: remove fmt dependency, numops.go, helper funcs for unsupported s…
XomaDev Mar 21, 2026
0ddf8a3
dev: runtime refactor, REPL, lexer and interpreter improvements
XomaDev Mar 21, 2026
3b52640
fix: strict type coercion, rename TryNum to CoerceNum, clean up metho…
XomaDev Mar 21, 2026
549eed7
dev: introduce NonConsumable type; statements no longer return values
XomaDev Mar 21, 2026
05f58f9
dev: drop sort, math/rand, encoding/csv — replace with own implementa…
XomaDev Mar 21, 2026
22260e9
finetune: generate cleansed problem statements
XomaDev Mar 21, 2026
ab42846
fine_tune: add more problem statements
XomaDev Mar 21, 2026
eee4b53
fine_tune: answer statements for PROBLEM1
XomaDev Mar 21, 2026
ab3435b
fine_tune: more answers
XomaDev Mar 21, 2026
1cbcee6
fine_tune: more answers
XomaDev Mar 21, 2026
3813129
fine_tune: more answers
XomaDev Mar 21, 2026
0a1e7e9
fine_tune: more answers
XomaDev Mar 21, 2026
d68bc9e
fine_tune: more answers
XomaDev Mar 21, 2026
8e62e9f
fine_tune: more answers
XomaDev Mar 21, 2026
f3b4cc7
fine_tune: more answers
XomaDev Mar 21, 2026
b18b1e1
fine_tune: more answers
XomaDev Mar 21, 2026
4172e17
fine_tune: more answers
XomaDev Mar 21, 2026
7a75422
fine_tune: more answers
XomaDev Mar 21, 2026
dfa0212
fine_tune: more answers
XomaDev Mar 21, 2026
33184fd
fine_tune: more answers
XomaDev Mar 21, 2026
0005ef8
fine_tune: more answers
XomaDev Mar 21, 2026
8ff4b50
fine_tune: more answers
XomaDev Mar 21, 2026
7215f04
fine_tune: more answers
XomaDev Mar 21, 2026
dfc8433
fine_tune: more answers
XomaDev Mar 21, 2026
c84d51b
fine_tune: MASTER.json file
XomaDev Mar 21, 2026
2536af1
fine_tune: clean MASTER.json to 1868 verified-compiling entries
XomaDev Mar 22, 2026
f8329e0
fix: left-associativity for + and - operators in parser
XomaDev Mar 22, 2026
10a6c64
fine_tune: add MASTER.txt and MASTER.jsonl export formats
XomaDev Mar 22, 2026
58ea3ed
fine_tune: wrap assistant code in ```falcon blocks in MASTER.jsonl
XomaDev Mar 22, 2026
d4549d8
fine_tune: reformat all MASTER.json code via Falcon AST printer
XomaDev Mar 22, 2026
e1b6ce8
fine_tune: reasoning traces
XomaDev Mar 23, 2026
87e06a0
fine_tune: add reasoning traces
XomaDev Mar 23, 2026
ba63c43
fine_tune: add reasoning traces for chunks 07-12 (600 entries)
XomaDev Mar 23, 2026
d29e3e2
fine_tune: add reasoning traces for chunk 13 (100 entries, idx 1310-1…
XomaDev Mar 23, 2026
34c3777
fine_tune: complete reasoning traces for all 1850 entries (chunks 14-…
XomaDev Mar 24, 2026
6952c3f
fine_tune: revise codes
XomaDev Mar 24, 2026
85002cf
fine_tune: fix 106 broken entries, rewrite think blocks, normalise sy…
XomaDev Mar 24, 2026
a931dd0
fine_tune: fix formatter bug and re-normalise all 1850 entries
XomaDev Mar 24, 2026
452fa9c
fix: smart_body Var handling + fix all 12 broken MASTER.jsonl entries
XomaDev Mar 24, 2026
b7f9abb
fine_tune: fix stuff
XomaDev Mar 24, 2026
798b742
fix: reject nested func definitions + flatten 5 MASTER.jsonl entries
XomaDev Mar 27, 2026
0af725f
add enrich script
XomaDev Mar 27, 2026
fb05c29
enrich
XomaDev Mar 27, 2026
78cd2ef
data cleaning
XomaDev Mar 28, 2026
4b7547a
fix: roundtrip 1863/1864 — VarResult flat output + RetProcedure wraps it
XomaDev Mar 29, 2026
7025283
new data
XomaDev Mar 29, 2026
7ac1478
final fixes
XomaDev Mar 29, 2026
89c9260
handle multiline for transform
XomaDev Mar 29, 2026
5568f5e
half trip again
XomaDev Mar 29, 2026
8b169f3
fix double nested braces problem
XomaDev Mar 29, 2026
4f8ef6d
new result jsonl
XomaDev Mar 29, 2026
d5b3b07
fix nested braces data
XomaDev Mar 29, 2026
ca0f7a2
cleanup files
XomaDev Mar 29, 2026
424459b
steps first, code later
XomaDev Mar 29, 2026
ab6194b
fix newline-blind postfix continuation in parser
XomaDev Mar 30, 2026
2212e79
add curriculum classifier and level-split dataset for epoch training
XomaDev Apr 1, 2026
7b9554f
add runtime error reporting with source line and caret
XomaDev Apr 2, 2026
93138ef
improve runtime and method error message quality
XomaDev Apr 2, 2026
4fe2145
route ast panics through token.Error() for source-location reporting
XomaDev Apr 2, 2026
771411e
fix transformer vs method call ambiguity when '{' follows a method
XomaDev Apr 2, 2026
91e0272
feat: yield functionality
XomaDev Apr 13, 2026
451c855
fix: restrict yield to result functions and document it
XomaDev Apr 13, 2026
1b99882
feat: resolve yield smt to native blky
XomaDev Apr 14, 2026
fa940cd
fix: properly handle single branch yield
XomaDev Apr 22, 2026
5e3ef94
fix: confirm yield properly
XomaDev Apr 22, 2026
90161dd
fix: check for available lastExprs for yield
XomaDev Apr 22, 2026
19c4e51
fix: re-refactor yield mechanism
XomaDev Apr 22, 2026
fbbd601
fix: wrap check for double wraps
XomaDev Apr 22, 2026
7a28a33
fix+refactor: error handling + engine problems
XomaDev Apr 27, 2026
e7dba4b
refactor: remove finetune/
XomaDev Apr 27, 2026
79f990b
remove orphan scripts
XomaDev Apr 27, 2026
a838ab4
fix: return proper signatures
XomaDev Apr 27, 2026
335049b
fix: signarture for Ifs
XomaDev Apr 27, 2026
1570358
chore: remove flags...bool arg from Consumable() methods
XomaDev Apr 27, 2026
9283e37
fix+refactor: signature system
XomaDev Apr 27, 2026
a26b1e8
feat: method and func name suggestions in errors
XomaDev Apr 27, 2026
5019fda
feat: code auto correct
XomaDev Apr 27, 2026
65a0c22
dev: auto correction for question call
XomaDev Apr 28, 2026
e86bf3b
refactor: more robust auto correction
XomaDev Apr 28, 2026
4c21f0d
fix+chore: fix problematic Consumable() handling for smart_body.go an…
XomaDev Apr 28, 2026
b99c815
fix: do not auto revert for now
XomaDev Apr 28, 2026
bcb937d
prepare: prepare refactor lang_parser.go
XomaDev Apr 28, 2026
429496f
refactor: make body() more welcoming to use
XomaDev Apr 28, 2026
8746a1a
implement yield parser
XomaDev May 21, 2026
b91d2f6
fix problems in yield parser
XomaDev May 22, 2026
668f977
wrap in result check when required
XomaDev May 22, 2026
b351bc1
fix: address the proper use of Var Result and Var Body
XomaDev May 22, 2026
aee0a56
fix: handle *variables.Var while traversing for yield
XomaDev May 22, 2026
6b76713
fix: handle variables.Var while expr traverse and check yield before …
XomaDev May 22, 2026
d52cd1e
fix: tiny bugs
XomaDev May 22, 2026
d208128
fix: add panics for nil bodies
XomaDev May 22, 2026
8ddf2f7
fix: infite loop while parsing loop yields
XomaDev May 22, 2026
4280279
fix: add EOF check for helper funcs in lexer.go
XomaDev May 22, 2026
db8778c
chore: remove test files
XomaDev May 22, 2026
1171149
fix+safety: scope checks for `break` and `yield` smts
XomaDev May 22, 2026
4a7a6e0
chore: cleanup binaries
XomaDev May 22, 2026
fde1639
fix: properly coerce indices
XomaDev May 22, 2026
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
43 changes: 36 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
![image|690x338](https://community.appinventor.mit.edu/uploads/default/original/3X/e/d/ed3b9d22ddaefffb4fd5ab71964b8816c56c63a1.png)

Falcon is a language designed for App Inventor to enable syntax-based programming and for incorporating agenting coding abilities.
Falcon is a language designed for App Inventor to enable syntax-based programming and for incorporating agentic coding abilities.

## Quirks
1. Falcon follows 1-based indexing.
2. Falcon variables are dynamically typed. Do not declare variables.
3. Lists and dictionaries are passed as references.
4. Falcon follows Kotlin's style of functional expressions.
5. Falcon does not have a return statement; the last expression in a body is returned.
5. Falcon does not have a traditional return statement. In result functions, the last expression is returned. Use `yield` inside a result function for an early return.
6. Falcon does NOT have a try-catch or a throw statement.
7. Only single-line comments using double slash `//` are supported.
8. Do not use `_` in place of unused variables
Expand Down Expand Up @@ -179,15 +179,44 @@ func double(n) = { n * 2 }
Or multiple expressions:

```
func FibSum(n) = {
func Fib(n) = {
if (n < 2) {
n
} else {
FibSum(n - 1) + FibSum(n - 2)
Fib(n - 1) + Fib(n - 2)
}
}
```
Note that there is no `return` statement in Falcon. The last statement in a body is taken as the output of an expression.
Note that there is no `return` statement in Falcon. The last expression in a body is taken as the output of an expression.

### yield

`yield <expr>` exits a result function early and returns the given value to the caller. It only works inside result functions (`= { ... }`). If no `yield` is reached, the last expression in the body is returned as usual.

```
func first_divisible(n, list) = {
for (i: 1..list.listLen()) {
if (list[i] % n == 0) {
yield list[i]
}
}
-1
}

println(first_divisible(7, [3, 10, 21, 44])) // Output: 21
println(first_divisible(7, [1, 2, 3])) // Output: -1
```

A common pattern is using `yield` as a guard clause:

```
func safe_div(a, b) = {
if (b == 0) {
yield 0
}
a / b
}
```

## Functions

Expand Down Expand Up @@ -298,7 +327,7 @@ e.g. `"Hello ".trim()`
### Dictionary

- `dictLen()`
- `get(key)`
- `get(key, notFound)`
- `set(key, value)`
- `delete(key)`
- `getAtPath(path_list, notfound)`
Expand Down Expand Up @@ -376,7 +405,7 @@ Usage `.min { m, n -> bool_m_preceeds_n }` and `.max { m, n -> bool_m_preceeds_n
local names = ["Bob", "Alice", "John"]
// Find the longest name
local longestName = names
.max { m, n -> n.textLen() > m.textLen() } // use min { } for the shortest name
.max { m, n -> m.textLen() < n.textLen() } // use min { } for the shortest name
println(longestName)
```

Expand Down
5 changes: 2 additions & 3 deletions lang/code/ast/blockly.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,8 @@ func ensureStatement(expr Expr) Block {
// First evaluate Blockly(). True indicates we expect a statement.
// This gives time for if expressions to mutate to if statement.
aBlock := expr.Blockly(true)
if expr.Consumable(true) {
// It's still consumable, wrap around evaluate but ignore result
return Block{Type: "controls_eval_but_ignore", Values: []Value{{Block: aBlock}}}
if expr.Consumable() {
panic("result of `" + expr.String() + "` is never used")
}
return aBlock
}
Expand Down
30 changes: 0 additions & 30 deletions lang/code/ast/blockly_tooling.go

This file was deleted.

56 changes: 50 additions & 6 deletions lang/code/ast/common/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,28 @@ type BinaryExpr struct {

func (b *BinaryExpr) String() string {
myPrecedence := lex.PrecedenceOf(b.Where.Flags[0])
// For non-left-associative operators, right operands at the same precedence need parens
// to avoid changing semantics: e.g. 10-(3-2) must not become 10-3-2.
rightNeedsParensAtSamePrec := b.Operator == lex.Dash || b.Operator == lex.Slash || b.Operator == lex.Power
stringified := make([]string, len(b.Operands))
for i, operand := range b.Operands {
operandStr := operand.String()
needsParens := false
// If operand is a BinaryExpr with lower precedence, wrap it
if binExpr, ok := operand.(*BinaryExpr); ok {
if lex.PrecedenceOf(binExpr.Where.Flags[0]) < myPrecedence {
operandStr = "(" + operandStr + ")"
opPrec := lex.PrecedenceOf(binExpr.Where.Flags[0])
if opPrec < myPrecedence {
needsParens = true
} else if i > 0 && rightNeedsParensAtSamePrec && opPrec == myPrecedence {
needsParens = true
}
} else if !operand.Continuous() {
// Non-continuous expressions (e.g. if-else) need parens as binary operands
needsParens = true
}
if needsParens {
operandStr = "(" + operandStr + ")"
}

stringified[i] = operandStr
}
return strings.Join(stringified, " "+*b.Where.Content+" ")
Expand All @@ -39,7 +51,10 @@ func (b *BinaryExpr) CanRepeat(testOperator lex.Type) bool {
return false
}
switch b.Operator {
case lex.Power, lex.Dash, lex.Slash:
case lex.Power, lex.Dash, lex.Slash,
lex.Equals, lex.NotEquals, lex.TextEquals, lex.TextNotEquals,
lex.LessThan, lex.LessThanEqual, lex.GreatThan, lex.GreaterThanEqual,
lex.TextLessThan, lex.TextGreaterThan:
return false
default:
return true
Expand Down Expand Up @@ -75,11 +90,23 @@ func (b *BinaryExpr) Continuous() bool {
return false
}

func (b *BinaryExpr) Consumable(flags ...bool) bool {
func (b *BinaryExpr) Consumable() bool {
return true
}

func (b *BinaryExpr) Signature() []ast.Signature {
switch b.Operator {
case lex.Plus, lex.Times, lex.Dash, lex.Slash, lex.Power, lex.Remainder, lex.BitwiseAnd, lex.BitwiseOr, lex.BitwiseXor:
b.ensureSignature(ast.SignNumb, ast.SignText)
case lex.LogicAnd, lex.LogicOr:
b.ensureSignature(ast.SignBool, ast.SignText)
case lex.Underscore:
// _ auto-converts any operand type to text at runtime; no type enforcement here.
case lex.TextEquals, lex.TextNotEquals, lex.TextLessThan, lex.TextGreaterThan:
b.ensureSignature(ast.SignText, ast.SignNumb)
case lex.LessThan, lex.LessThanEqual, lex.GreatThan, lex.GreaterThanEqual:
b.ensureSignature(ast.SignNumb, ast.SignText)
}
switch b.Operator {
case lex.BitwiseAnd, lex.BitwiseOr, lex.BitwiseXor:
return []ast.Signature{ast.SignNumb}
Expand All @@ -95,6 +122,8 @@ func (b *BinaryExpr) Signature() []ast.Signature {
return []ast.Signature{ast.SignText}
case lex.LessThan, lex.LessThanEqual, lex.GreatThan, lex.GreaterThanEqual:
return []ast.Signature{ast.SignBool}
case lex.Remainder:
return []ast.Signature{ast.SignNumb}
case lex.TextEquals, lex.TextNotEquals, lex.TextLessThan, lex.TextGreaterThan:
return []ast.Signature{ast.SignBool}
default:
Expand All @@ -103,6 +132,21 @@ func (b *BinaryExpr) Signature() []ast.Signature {
}
}

func (b *BinaryExpr) ensureSignature(acceptableSignature ...ast.Signature) {
if len(b.Operands) == 0 {
panic("BinaryExpr.ensureSignature: empty operands")
}
for _, op := range b.Operands {
opSigs := op.Signature()
for _, signature := range acceptableSignature {
if ast.HasSignature(opSigs, signature) {
return
}
}
}
b.Where.TypeError("Operator '%' requires % operand types", *b.Where.Content, ast.FormatSignatures(acceptableSignature))
}

func (b *BinaryExpr) textCompare() ast.Block {
var fieldOp string
switch b.Operator {
Expand All @@ -128,7 +172,7 @@ func (b *BinaryExpr) relationalExpr() ast.Block {
case lex.LessThan:
fieldOp = "LT"
case lex.LessThanEqual:
fieldOp = "LT"
fieldOp = "LTE"
case lex.GreatThan:
fieldOp = "GT"
case lex.GreaterThanEqual:
Expand Down
43 changes: 43 additions & 0 deletions lang/code/ast/common/drop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package common

import "strings"

// dropTypeWords lists the type-name suffixes (and direct function names) that
// constitute a no-op type-conversion in Falcon's dynamic type system.
// ".toString()", ".toStr()", ".toInt()", ".toNumber()" etc. are all identity
// operations that should be silently dropped from the AST.
var dropTypeWords = map[string]bool{
"string": true, "str": true, "text": true,
"int": true, "integer": true,
"num": true, "number": true, "float": true, "double": true,
"bool": true, "boolean": true,
"list": true, "array": true, "arr": true,
"dict": true, "map": true, "object": true, "obj": true,
}

// IsDropMethod reports whether a 0-arg method call named name is a no-op
// type conversion that should be silently dropped.
// Matches the "to<Type>" camelCase prefix pattern (e.g. toString, toStr,
// toInt, toNumber, toList, toDict, toBoolean, …).
func IsDropMethod(name string) bool {
lower := strings.ToLower(name)
if !strings.HasPrefix(lower, "to") || len(lower) <= 2 {
return false
}
return dropTypeWords[lower[2:]]
}

// IsDropFunction reports whether a 1-arg function call named name is a no-op
// type-cast wrapper that should be dropped, keeping only its argument.
// Matches both bare type names (string, int, number, …) and the "to<Type>"
// prefix form (toString, toInt, …).
func IsDropFunction(name string) bool {
lower := strings.ToLower(name)
if dropTypeWords[lower] {
return true
}
if strings.HasPrefix(lower, "to") && len(lower) > 2 {
return dropTypeWords[lower[2:]]
}
return false
}
4 changes: 2 additions & 2 deletions lang/code/ast/common/empty_socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ func (e *EmptySocket) Continuous() bool {
return true
}

func (e *EmptySocket) Consumable(flags ...bool) bool {
func (e *EmptySocket) Consumable() bool {
return false
}

func (e *EmptySocket) Signature() []ast.Signature {
return []ast.Signature{ast.SignText}
return []ast.Signature{ast.SignNumb}
}
Loading