Skip to content

Commit

Permalink
Add stub for forth (#54)
Browse files Browse the repository at this point in the history
* forth setup

* update authors

* add forth solution (greetings from twitch live)
  • Loading branch information
lucaferranti authored May 3, 2024
1 parent a0eb5bf commit 9861a7d
Show file tree
Hide file tree
Showing 8 changed files with 627 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,14 @@
"prerequisites": [],
"difficulty": 1
},
{
"slug": "forth",
"name": "Forth",
"uuid": "48dd93c3-6fa4-4e9c-a973-6fdbd3a640a5",
"practices": [],
"prerequisites": [],
"difficulty": 10
},
{
"slug": "pangram",
"name": "Pangram",
Expand Down
23 changes: 23 additions & 0 deletions exercises/practice/forth/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Instructions

Implement an evaluator for a very simple subset of Forth.

[Forth][forth]
is a stack-based programming language.
Implement a very basic evaluator for a small subset of Forth.

Your evaluator has to support the following words:

- `+`, `-`, `*`, `/` (integer arithmetic)
- `DUP`, `DROP`, `SWAP`, `OVER` (stack manipulation)

Your evaluator also has to support defining new words using the customary syntax: `: word-name definition ;`.

To keep things simple the only data type you need to support is signed integers of at least 16 bits size.

You should use the following rules for the syntax: a number is a sequence of one or more (ASCII) digits, a word is a sequence of one or more letters, digits, symbols or punctuation that is not a number.
(Forth probably uses slightly different rules, but this is close enough.)

Words are case-insensitive.

[forth]: https://en.wikipedia.org/wiki/Forth_%28programming_language%29
16 changes: 16 additions & 0 deletions exercises/practice/forth/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"authors": ["lucaferranti"],
"contributors": ["kytrinyx"],
"files": {
"solution": [
"src/forth.chpl"
],
"test": [
"test/tests.chpl"
],
"example": [
".meta/reference.chpl"
]
},
"blurb": "Implement an evaluator for a very simple subset of Forth."
}
85 changes: 85 additions & 0 deletions exercises/practice/forth/.meta/reference.chpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
module Forth {

use List;
use Map;

const BINARY_OPS = ["+", "-", "*", "/", "swap", "over"];
const UNARY_OPS = ["drop", "dup"];

proc evaluate(statements: [] string) throws {
var stack: list(int);
var env: map(string, string);
for statement in statements {
var instructions = statement.toLower().split();
if instructions[0] == ":" && instructions[instructions.size-1] == ";" {
parse_variable_assignment(env, instructions);
} else {
for instruction in instructions {
evaluate_instruction(stack, env, instruction);
}
}
}
return stack;
}

proc parse_variable_assignment(ref env: map(string, string), instructions: [] string) throws {
const varname = instructions[1],
varvalues = instructions[2..instructions.size-2];
if isNumber(varname) then throw new IllegalArgumentError("illegal operation");
var substitued_instr = " ".join([varvalue in varvalues] env.get(varvalue, varvalue));
if env.contains(varname) then env.replace(varname, substitued_instr);
else env.add(varname, substitued_instr);
}

proc evaluate_instruction(ref stack: list(int), env: map(string, string), instr: string): void throws {
if env.contains(instr) {
var new_instrs = env.get(instr, "").split();
for new_instr in new_instrs {
evaluate_instruction(stack, env, new_instr);
}
} else execute_instruction(stack, instr);
}

proc execute_instruction(ref stack: list(int), instr: string) throws {
if isNumber(instr) then stack.pushBack(instr: int);
else if BINARY_OPS.find(instr) > -1 then execute_binary_operation(stack, instr);
else if UNARY_OPS.find(instr) > -1 then execute_unary_operation(stack, instr);
else throw new IllegalArgumentError("undefined operation");
}

proc execute_binary_operation(ref stack: list(int), instr: string) throws {
if stack.isEmpty() then throw new IllegalArgumentError("empty stack");
if stack.size == 1 then throw new IllegalArgumentError("only one value on the stack");

const op1 = stack.popBack(),
op2 = stack.popBack();

select instr {
when "+" do stack.pushBack(op1 + op2);
when "-" do stack.pushBack(op2 - op1);
when "*" do stack.pushBack(op1 * op2);
when "/" {
if op1 == 0 then throw new IllegalArgumentError("divide by zero");
else stack.pushBack(op2 / op1);
}
when "swap" do stack.pushBack([op1, op2]);
when "over" do stack.pushBack([op2, op1, op2]);
otherwise throw new IllegalArgumentError("How did you even get here?");
}
}

proc execute_unary_operation(ref stack: list(int), instr: string) throws {
if stack.isEmpty() then throw new IllegalArgumentError("empty stack");

select instr {
when "drop" do stack.popBack();
otherwise do stack.pushBack(stack[stack.size-1]); // dup
}
}

proc isNumber(s: string) {
if s.startsWith("+") || s.startsWith("-") then return s[1..].isDigit();
else return s.isDigit();
}

}
157 changes: 157 additions & 0 deletions exercises/practice/forth/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[9962203f-f00a-4a85-b404-8a8ecbcec09d]
description = "parsing and numbers -> numbers just get pushed onto the stack"

[fd7a8da2-6818-4203-a866-fed0714e7aa0]
description = "parsing and numbers -> pushes negative numbers onto the stack"

[9e69588e-a3d8-41a3-a371-ea02206c1e6e]
description = "addition -> can add two numbers"

[52336dd3-30da-4e5c-8523-bdf9a3427657]
description = "addition -> errors if there is nothing on the stack"

[06efb9a4-817a-435e-b509-06166993c1b8]
description = "addition -> errors if there is only one value on the stack"

[09687c99-7bbc-44af-8526-e402f997ccbf]
description = "subtraction -> can subtract two numbers"

[5d63eee2-1f7d-4538-b475-e27682ab8032]
description = "subtraction -> errors if there is nothing on the stack"

[b3cee1b2-9159-418a-b00d-a1bb3765c23b]
description = "subtraction -> errors if there is only one value on the stack"

[5df0ceb5-922e-401f-974d-8287427dbf21]
description = "multiplication -> can multiply two numbers"

[9e004339-15ac-4063-8ec1-5720f4e75046]
description = "multiplication -> errors if there is nothing on the stack"

[8ba4b432-9f94-41e0-8fae-3b3712bd51b3]
description = "multiplication -> errors if there is only one value on the stack"

[e74c2204-b057-4cff-9aa9-31c7c97a93f5]
description = "division -> can divide two numbers"

[54f6711c-4b14-4bb0-98ad-d974a22c4620]
description = "division -> performs integer division"

[a5df3219-29b4-4d2f-b427-81f82f42a3f1]
description = "division -> errors if dividing by zero"

[1d5bb6b3-6749-4e02-8a79-b5d4d334cb8a]
description = "division -> errors if there is nothing on the stack"

[d5547f43-c2ff-4d5c-9cb0-2a4f6684c20d]
description = "division -> errors if there is only one value on the stack"

[ee28d729-6692-4a30-b9be-0d830c52a68c]
description = "combined arithmetic -> addition and subtraction"

[40b197da-fa4b-4aca-a50b-f000d19422c1]
description = "combined arithmetic -> multiplication and division"

[c5758235-6eef-4bf6-ab62-c878e50b9957]
description = "dup -> copies a value on the stack"

[f6889006-5a40-41e7-beb3-43b09e5a22f4]
description = "dup -> copies the top value on the stack"

[40b7569c-8401-4bd4-a30d-9adf70d11bc4]
description = "dup -> errors if there is nothing on the stack"

[1971da68-1df2-4569-927a-72bf5bb7263c]
description = "drop -> removes the top value on the stack if it is the only one"

[8929d9f2-4a78-4e0f-90ad-be1a0f313fd9]
description = "drop -> removes the top value on the stack if it is not the only one"

[6dd31873-6dd7-4cb8-9e90-7daa33ba045c]
description = "drop -> errors if there is nothing on the stack"

[3ee68e62-f98a-4cce-9e6c-8aae6c65a4e3]
description = "swap -> swaps the top two values on the stack if they are the only ones"

[8ce869d5-a503-44e4-ab55-1da36816ff1c]
description = "swap -> swaps the top two values on the stack if they are not the only ones"

[74ba5b2a-b028-4759-9176-c5c0e7b2b154]
description = "swap -> errors if there is nothing on the stack"

[dd52e154-5d0d-4a5c-9e5d-73eb36052bc8]
description = "swap -> errors if there is only one value on the stack"

[a2654074-ba68-4f93-b014-6b12693a8b50]
description = "over -> copies the second element if there are only two"

[c5b51097-741a-4da7-8736-5c93fa856339]
description = "over -> copies the second element if there are more than two"

[6e1703a6-5963-4a03-abba-02e77e3181fd]
description = "over -> errors if there is nothing on the stack"

[ee574dc4-ef71-46f6-8c6a-b4af3a10c45f]
description = "over -> errors if there is only one value on the stack"

[ed45cbbf-4dbf-4901-825b-54b20dbee53b]
description = "user-defined words -> can consist of built-in words"

[2726ea44-73e4-436b-bc2b-5ff0c6aa014b]
description = "user-defined words -> execute in the right order"

[9e53c2d0-b8ef-4ad8-b2c9-a559b421eb33]
description = "user-defined words -> can override other user-defined words"

[669db3f3-5bd6-4be0-83d1-618cd6e4984b]
description = "user-defined words -> can override built-in words"

[588de2f0-c56e-4c68-be0b-0bb1e603c500]
description = "user-defined words -> can override built-in operators"

[ac12aaaf-26c6-4a10-8b3c-1c958fa2914c]
description = "user-defined words -> can use different words with the same name"

[53f82ef0-2750-4ccb-ac04-5d8c1aefabb1]
description = "user-defined words -> can define word that uses word with the same name"

[35958cee-a976-4a0f-9378-f678518fa322]
description = "user-defined words -> cannot redefine non-negative numbers"

[df5b2815-3843-4f55-b16c-c3ed507292a7]
description = "user-defined words -> cannot redefine negative numbers"

[5180f261-89dd-491e-b230-62737e09806f]
description = "user-defined words -> errors if executing a non-existent word"

[3c8bfef3-edbb-49c1-9993-21d4030043cb]
description = "user-defined words -> only defines locally"

[7b83bb2e-b0e8-461f-ad3b-96ee2e111ed6]
description = "case-insensitivity -> DUP is case-insensitive"

[339ed30b-f5b4-47ff-ab1c-67591a9cd336]
description = "case-insensitivity -> DROP is case-insensitive"

[ee1af31e-1355-4b1b-bb95-f9d0b2961b87]
description = "case-insensitivity -> SWAP is case-insensitive"

[acdc3a49-14c8-4cc2-945d-11edee6408fa]
description = "case-insensitivity -> OVER is case-insensitive"

[5934454f-a24f-4efc-9fdd-5794e5f0c23c]
description = "case-insensitivity -> user-defined words are case-insensitive"

[037d4299-195f-4be7-a46d-f07ca6280a06]
description = "case-insensitivity -> definitions are case-insensitive"
5 changes: 5 additions & 0 deletions exercises/practice/forth/Mason.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[brick]
name="forth"
version="0.1.0"
chplVersion="1.33.0"
type="application"
3 changes: 3 additions & 0 deletions exercises/practice/forth/src/forth.chpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Forth {
// write your solution here
}
Loading

0 comments on commit 9861a7d

Please sign in to comment.