init
This commit is contained in:
69
.gitignore
vendored
Normal file
69
.gitignore
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
# ---> CMake
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
CMakeUserPresets.json
|
||||
|
||||
# ---> C
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
build
|
||||
4
CMakeLists.txt
Normal file
4
CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(PS1)
|
||||
|
||||
add_executable(ps1 src/driver.c)
|
||||
18
LICENSE
Normal file
18
LICENSE
Normal file
@@ -0,0 +1,18 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 frero-uni
|
||||
|
||||
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, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
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. IN NO
|
||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1769461804,
|
||||
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
23
flake.nix
Normal file
23
flake.nix
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
description = "DevShell with flex, bison, gdb, cmake";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
outputs =
|
||||
{ self, nixpkgs }:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
devShells.${system}.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
flex
|
||||
bison
|
||||
gdb
|
||||
cmake
|
||||
libgcc
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
89
src/driver.c
Normal file
89
src/driver.c
Normal file
@@ -0,0 +1,89 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "table.c"
|
||||
|
||||
static_assert(START >= 0 && START < NSTATES, "START must be a valid state");
|
||||
static_assert(ACCEPT >= 0 && ACCEPT < NSTATES, "ACCEPT must be a valid state");
|
||||
static_assert(ERROR >= 0 && ERROR < NSTATES, "ERROR must be a valid state");
|
||||
|
||||
// This program takes in lines from stdin and validates them line by line.
|
||||
// If a line is valid, it is ignored.
|
||||
// Otherwise, it is printed in full,
|
||||
// followed by a line indicating at which character the error occurs.
|
||||
int main() {
|
||||
|
||||
// Fill the table first. This function does no input processing only table creation.
|
||||
fillTable();
|
||||
printf("Table filled!\n");
|
||||
|
||||
// Lines may not be more than 512 lines long
|
||||
char line[512];
|
||||
int lineNum = 0;
|
||||
|
||||
// For each line in input
|
||||
while (fgets(line, sizeof(line), stdin)) {
|
||||
lineNum++;
|
||||
|
||||
int state = START;
|
||||
int pos = 0;
|
||||
bool fatalError = false;
|
||||
while (true) {
|
||||
unsigned char c = line[pos];
|
||||
|
||||
// If the line ended without a newline, patch it in
|
||||
if (c == '\0')
|
||||
c = '\n';
|
||||
|
||||
// Use the symbol to go to the next state
|
||||
state = table[state][c];
|
||||
|
||||
// Uncomment this for debugging:
|
||||
/* printf("State %i got c=%c\n", state, c); */
|
||||
|
||||
if (state < 0 || state >= NSTATES) {
|
||||
printf("fatal error: DFS entered an invalid state: %d\n", state);
|
||||
fatalError = true;
|
||||
}
|
||||
else if (state == ACCEPT) {
|
||||
// Make sure statement is accepted at new line
|
||||
if (c == '\n') {
|
||||
printf("line %4d: accepted: %.*s\n", lineNum, pos, line);
|
||||
|
||||
// Break inner loop
|
||||
break;
|
||||
}
|
||||
|
||||
printf("fatal error: ACCEPT state reached before end of line:\n");
|
||||
fatalError = true;
|
||||
}
|
||||
else if (c == '\n' && state != ERROR) {
|
||||
printf("fatal error: Line ended without reaching either ACCEPT or ERROR state: %d\n", state);
|
||||
fatalError = true;
|
||||
}
|
||||
|
||||
// Provide the position we were at in the string when the DFS reached the ERROR state,
|
||||
// or when a fatal error occured. The latter can only happen if the DFS is broken.
|
||||
if (state == ERROR || fatalError) {
|
||||
printf("line %4d: error: %s", lineNum, line);
|
||||
printf("~~~~~~~~~~~~~~~~~~");
|
||||
for(int i = 0; i < pos; i++)
|
||||
putc('~', stdout);
|
||||
printf("^\n");
|
||||
break;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
// Fatal errors means there is something wrong with the DFS table. Stop processing lines!
|
||||
if (fatalError) {
|
||||
return 1; // return code 1 means error
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
40
src/table.c
Normal file
40
src/table.c
Normal file
@@ -0,0 +1,40 @@
|
||||
// The number of states in your table
|
||||
#define NSTATES 14
|
||||
|
||||
// The starting state, at the beginning of each line
|
||||
#define START 0
|
||||
|
||||
// The state to go to after a valid line
|
||||
// All lines end with the newline character '\n'
|
||||
#define ACCEPT 12
|
||||
|
||||
// The state to jump to as soon as a line is invalid
|
||||
#define ERROR 13
|
||||
|
||||
int table[NSTATES][256];
|
||||
|
||||
void fillTable() {
|
||||
|
||||
// Make all states lead to ERROR by default
|
||||
for (int i = 0; i < NSTATES; i++) {
|
||||
for (int c = 0; c < 256; c++) {
|
||||
table[i][c] = ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip whitespace
|
||||
table[START][' '] = START;
|
||||
|
||||
// If we reach a newline, and are not in the middle of a statement, accept
|
||||
table[START]['\n'] = ACCEPT;
|
||||
|
||||
// Accept the statement "go"
|
||||
table[START]['g'] = 1;
|
||||
table[1]['o'] = 2;
|
||||
table[2]['\n'] = ACCEPT;
|
||||
|
||||
|
||||
// TODO Expand the table to pass (and fail) the described syntax
|
||||
// table[...][...] = ...
|
||||
|
||||
}
|
||||
11
testing/expected/0-go.txt
Normal file
11
testing/expected/0-go.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Table filled!
|
||||
line 1: accepted: go
|
||||
line 2: error: apple
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
line 3: accepted: go
|
||||
line 4: accepted:
|
||||
line 5: error: banana
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
line 6: accepted: go
|
||||
line 7: error: orange
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
9
testing/expected/1-spaces.txt
Normal file
9
testing/expected/1-spaces.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Table filled!
|
||||
line 1: accepted: go
|
||||
line 2: accepted:
|
||||
line 3: accepted: go go
|
||||
line 4: error: go gogo
|
||||
~~~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 5: accepted: go go go go go go
|
||||
line 6: error: gogo gogo
|
||||
~~~~~~~~~~~~~~~~~~~~^
|
||||
21
testing/expected/2-dxdy.txt
Normal file
21
testing/expected/2-dxdy.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
Table filled!
|
||||
line 1: accepted: dx=2
|
||||
line 2: accepted: dy=3
|
||||
line 3: accepted: dx=9
|
||||
line 4: accepted: dx=55
|
||||
line 5: accepted: dy=09
|
||||
line 6: accepted: dx=5 dx=1 go
|
||||
line 7: error: da=1
|
||||
~~~~~~~~~~~~~~~~~~~^
|
||||
line 8: error: indy=1
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
line 9: accepted: dx=999
|
||||
line 10: accepted: dy=-10
|
||||
line 11: error: dx=--5
|
||||
~~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 12: error: dx=-
|
||||
~~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 13: error: dy=
|
||||
~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 14: accepted: dx=-0 dy=55 go dx=197 dy=-2
|
||||
line 15: accepted: dx=20 dy=20
|
||||
16
testing/expected/3-labels.txt
Normal file
16
testing/expected/3-labels.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
Table filled!
|
||||
line 1: accepted: 8: go
|
||||
line 2: accepted: 10: dx=1
|
||||
line 3: accepted: 5:go dx=2
|
||||
line 4: accepted: 2:
|
||||
line 5: error: -11: go go
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
line 6: accepted: 100: go dx=5
|
||||
line 7: error: apple: go
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
line 8: error: go 4:
|
||||
~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 9: error: 22: 22: go
|
||||
~~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 10: error: : go
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
14
testing/expected/4-comments.txt
Normal file
14
testing/expected/4-comments.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
Table filled!
|
||||
line 1: accepted: // This file contains tests
|
||||
line 2: accepted: //Spacesbeforecommentshouldnotmatter
|
||||
line 3: accepted: go go go // This should be accepted
|
||||
line 4: accepted: 10: dx=10 dy=-5 go // This should also be accepted
|
||||
line 5: error: dx=-1 dy=5 go /* This should fail! */
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 6: accepted: go go // dx=-4.2 should be accepted
|
||||
line 7: accepted: //
|
||||
line 8: accepted: go// Comments can be right next to statements
|
||||
line 9: accepted: dx=5// Here as well
|
||||
line 10: accepted: 7://Labels too!
|
||||
line 11: error: go / We need two slashes
|
||||
~~~~~~~~~~~~~~~~~~~~~~^
|
||||
9
testing/expected/5-mixed_ok.txt
Normal file
9
testing/expected/5-mixed_ok.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Table filled!
|
||||
line 1: accepted: go go dx=1 go
|
||||
line 2: accepted: go// OK
|
||||
line 3: accepted: 9: dx=-1 //
|
||||
line 4: accepted: 10: //
|
||||
line 5: accepted: 11://
|
||||
line 6: accepted: dy=-9 // Not dy=--9
|
||||
line 7: accepted: 00: dx=-0001 go
|
||||
line 8: accepted: 94124543535: dx=94124543535 go dy=-111 go // End
|
||||
21
testing/expected/6-mixed_err.txt
Normal file
21
testing/expected/6-mixed_err.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
Table filled!
|
||||
line 1: error: dx= // Comment
|
||||
~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 2: error: go go goooo
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 3: error: dy=11dx=11
|
||||
~~~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 4: error: -5: go
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
line 5: error: : // Empty label?
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
line 6: error: 5:4:
|
||||
~~~~~~~~~~~~~~~~~~~~^
|
||||
line 7: error: go / Comment
|
||||
~~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 8: error: 8: dy=42 dx=4.2
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
|
||||
line 9: error: I beheld the wretch—the miserable monster whom I had created
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
line 10: error: apple pie
|
||||
~~~~~~~~~~~~~~~~~~^
|
||||
87
testing/test.py
Normal file
87
testing/test.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import difflib
|
||||
import pathlib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
ps1_root_dir = pathlib.Path(__file__).parent.parent.resolve()
|
||||
|
||||
build_dir = ps1_root_dir / "build"
|
||||
|
||||
print("Building and compiling...")
|
||||
try:
|
||||
subprocess.run(["cmake", str(ps1_root_dir), "-B", str(build_dir)], check=True)
|
||||
subprocess.run(["cmake", "--build", str(build_dir)], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"An error occurred while running CMake: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
print("Build complete")
|
||||
|
||||
testing_dir = ps1_root_dir / "testing"
|
||||
tests_dir = testing_dir / "tests"
|
||||
outputs_dir = testing_dir / "expected"
|
||||
|
||||
executable = build_dir / "ps1"
|
||||
|
||||
sorted_test_files : list[pathlib.Path] = []
|
||||
|
||||
for test_file in tests_dir.glob("*.txt"):
|
||||
sorted_test_files.append(test_file)
|
||||
|
||||
sorted_test_files.sort(key= lambda p : p.name)
|
||||
|
||||
passed = 0
|
||||
for test_file in sorted_test_files:
|
||||
name = test_file.name
|
||||
print(f"Testing {name}... ", end="")
|
||||
output_file = outputs_dir / name
|
||||
|
||||
if not output_file.exists():
|
||||
print("")
|
||||
print(f"Error: Could not find {str(output_file)}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
result : list[str] = []
|
||||
with test_file.open("r") as test_input:
|
||||
result_str : str = subprocess.run(
|
||||
[executable],
|
||||
stdin=test_input,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True, # Output must be text
|
||||
check=True
|
||||
).stdout
|
||||
|
||||
result = result_str.splitlines()
|
||||
|
||||
expected : list[str] = []
|
||||
with output_file.open("r") as expected_output:
|
||||
expected = expected_output.read().splitlines()
|
||||
|
||||
diff_list : list[str] = []
|
||||
for diff in difflib.unified_diff(result, expected, fromfile='your output', tofile='expected_output', lineterm='', n=3):
|
||||
diff_list.append(diff)
|
||||
|
||||
if not diff_list:
|
||||
# List is empty; no diffs
|
||||
passed += 1
|
||||
print("OK")
|
||||
continue
|
||||
|
||||
# If at least one diff, print
|
||||
print("MISMATCH")
|
||||
for diff in diff_list:
|
||||
print(diff)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("")
|
||||
print(f"Error: Test {name} failed to execute: {e}", file=sys.stderr)
|
||||
|
||||
print(f"{passed}/{len(sorted_test_files)} tests passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
7
testing/tests/0-go.txt
Normal file
7
testing/tests/0-go.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
go
|
||||
apple
|
||||
go
|
||||
|
||||
banana
|
||||
go
|
||||
orange
|
||||
6
testing/tests/1-spaces.txt
Normal file
6
testing/tests/1-spaces.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
go
|
||||
|
||||
go go
|
||||
go gogo
|
||||
go go go go go go
|
||||
gogo gogo
|
||||
15
testing/tests/2-dxdy.txt
Normal file
15
testing/tests/2-dxdy.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
dx=2
|
||||
dy=3
|
||||
dx=9
|
||||
dx=55
|
||||
dy=09
|
||||
dx=5 dx=1 go
|
||||
da=1
|
||||
indy=1
|
||||
dx=999
|
||||
dy=-10
|
||||
dx=--5
|
||||
dx=-
|
||||
dy=
|
||||
dx=-0 dy=55 go dx=197 dy=-2
|
||||
dx=20 dy=20
|
||||
10
testing/tests/3-labels.txt
Normal file
10
testing/tests/3-labels.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
8: go
|
||||
10: dx=1
|
||||
5:go dx=2
|
||||
2:
|
||||
-11: go go
|
||||
100: go dx=5
|
||||
apple: go
|
||||
go 4:
|
||||
22: 22: go
|
||||
: go
|
||||
11
testing/tests/4-comments.txt
Normal file
11
testing/tests/4-comments.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
// This file contains tests
|
||||
//Spacesbeforecommentshouldnotmatter
|
||||
go go go // This should be accepted
|
||||
10: dx=10 dy=-5 go // This should also be accepted
|
||||
dx=-1 dy=5 go /* This should fail! */
|
||||
go go // dx=-4.2 should be accepted
|
||||
//
|
||||
go// Comments can be right next to statements
|
||||
dx=5// Here as well
|
||||
7://Labels too!
|
||||
go / We need two slashes
|
||||
8
testing/tests/5-mixed_ok.txt
Normal file
8
testing/tests/5-mixed_ok.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
go go dx=1 go
|
||||
go// OK
|
||||
9: dx=-1 //
|
||||
10: //
|
||||
11://
|
||||
dy=-9 // Not dy=--9
|
||||
00: dx=-0001 go
|
||||
94124543535: dx=94124543535 go dy=-111 go // End
|
||||
10
testing/tests/6-mixed_err.txt
Normal file
10
testing/tests/6-mixed_err.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
dx= // Comment
|
||||
go go goooo
|
||||
dy=11dx=11
|
||||
-5: go
|
||||
: // Empty label?
|
||||
5:4:
|
||||
go / Comment
|
||||
8: dy=42 dx=4.2
|
||||
I beheld the wretch—the miserable monster whom I had created
|
||||
apple pie
|
||||
Reference in New Issue
Block a user