Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: List of Cairo examples to select from - Issue #109 #116

Merged
62 changes: 57 additions & 5 deletions components/Editor/EditorControls.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import { useRef } from 'react'
import { useMemo, useRef, useId } from 'react'

import { RiLinksLine, RiQuestionLine } from '@remixicon/react'
import cn from 'classnames'
import { Priority, useRegisterActions } from 'kbar'
import Select, { OnChangeValue } from 'react-select'
import examples from 'components/Editor/examples'

import { Button, Input } from 'components/ui'

type SelectOption = {
value: number
label: string
}

type EditorControlsProps = {
isCompileDisabled: boolean
programArguments: string
areProgramArgumentsValid: boolean
exampleName: number
handleChangeExampleOption: (
option: OnChangeValue<SelectOption, false>,
) => void
onCopyPermalink: () => void
onCompileRun: () => void
onProgramArgumentsUpdate: (args: string) => void
Expand All @@ -20,6 +31,8 @@ const EditorControls = ({
isCompileDisabled,
programArguments,
areProgramArgumentsValid,
exampleName,
handleChangeExampleOption,
onCopyPermalink,
onCompileRun,
onProgramArgumentsUpdate,
Expand Down Expand Up @@ -56,6 +69,33 @@ const EditorControls = ({

useRegisterActions(actions, [onCompileRun, onCopyPermalink])

const CairoNameExamples = useMemo(
() => [
'Default',
'Variables & mutability',
'Type casting',
'Control flow',
'Functions',
'Arrays',
'Dictionaries',
'Ownership',
],
[],
)

const examplesOptions = examples.Cairo.map((example, i) => ({
value: i,
label: CairoNameExamples[i],
}))

const exampleNameValue = useMemo(
() => ({
value: exampleName,
label: CairoNameExamples[exampleName],
}),
[CairoNameExamples, exampleName],
)

return (
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-x-4 px-4 py-4 md:py-2 md:border-r border-gray-200 dark:border-black-500">
<div className="flex flex-col md:flex-row md:gap-x-4 gap-y-2 md:gap-y-0 mb-4 md:mb-0">
Expand All @@ -71,7 +111,18 @@ const EditorControls = ({
</span>
</Button>
</div>

<div className="w-full md:w-60 lg:mr-20">
<Select
isSearchable={false}
classNamePrefix="select"
menuPlacement="auto"
value={exampleNameValue}
options={examplesOptions}
instanceId={useId()}
onChange={handleChangeExampleOption}
isDisabled={isCompileDisabled}
/>
</div>
<Input
ref={inputRef}
rightIcon={
Expand All @@ -87,23 +138,24 @@ const EditorControls = ({
}}
readOnly={isCompileDisabled}
value={programArguments}
placeholder={`Enter program arguments...`}
className={cn('grow border bg-gray-200 dark:bg-gray-800', {
placeholder={`Program arguments`}
className={cn('grow border bg-gray-200 dark:bg-gray-800 ', {
'dark:border-gray-800 border-gray-200': areProgramArgumentsValid,
'border-red-500': !areProgramArgumentsValid,
})}
inputClassName={cn({
'text-red-500': !areProgramArgumentsValid,
})}
/>

<div>
<Button
onClick={onCompileRun}
disabled={isCompileDisabled || !areProgramArgumentsValid}
size="sm"
contentClassName="justify-center"
>
Compile and run
Run
</Button>
</div>
</div>
Expand Down
187 changes: 187 additions & 0 deletions components/Editor/examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,193 @@ const examples: ExampleCode = {
fn main() -> felt252 {
let n = 2 + 3;
n
}`,
`use core::felt252;

const my_constant: felt252 = 42;

fn main() {

// non-mutable variable
let my_var = 12;

println!("{my_var}");

// my_var = 38; <-- fails to compile

// variable shadowing (declare another variable with the same name)
let my_var = 'hello world';

println!("{my_var}");

// mutable variable
let mut my_mut_var = 10;
my_mut_var = my_mut_var * 2;

println!("{my_mut_var}");

// my_mut_var = 'hello world' <-- fails to compile
}`,
`use core::felt252;

fn main() {
let my_felt252 = 10;

// Since a felt252 might not fit in a u8, we need to unwrap the Option<T> type
let my_u8: u8 = my_felt252.try_into().unwrap();

let my_u16: u16 = my_u8.into();
let my_u32: u32 = my_u16.into();
let my_u64: u64 = my_u32.into();
let _my_u128: u128 = my_u64.into();

// As a felt252 is smaller than a u256, we can use the into() method
let _my_u256: u256 = my_felt252.into();
let _my_usize: usize = my_felt252.try_into().unwrap();
let _my_other_felt252: felt252 = my_u8.into();
let _my_third_felt252: felt252 = my_u16.into();
}`,
`#[derive(Drop)]
enum Direction {
Up,
Down,
Left,
Right
}

fn main() {

// if / else expression
let x = 10;
let y = 20;

if x == y {
println!("x == y");
}
else {
println!("x != y");
};

// if with return value
let _res = if x == y {
'equal'
}
else {
'not_equal'
};

// match expression (with some limitations in Cairo <= 2.5)
let x = Direction::Up;
match x {
Direction::Up => println!("you win !"),
_ => println!("you loose ...")
}

// match expression with return value
let x = Direction::Down;
let _res = match x {
Direction::Up => 1,
Direction::Down => -1,
Direction::Left => -1,
Direction::Right => 1,
};

// loop expression

let mut i: u128 = 0;
loop {
if i > 9 { // Break condition
break;
}

i = i + 1;
};

// loop with return value
let _res = loop {
if i > 9 { break (42); }
i = i + 1;
};

}`,
`// This function returns an u32.
fn add(a: u32, b: u32) -> u64 {

// there is no semi-colon at the end so the
// result of this expression is returned.
// equivalent to: return x + 1;
a.into() + b.into()
}

// This functions doesn't return anything.
fn main() {
let a = 1;
let b = 2;
let x = 3;
let y = 4;

// named parameters to be more explicit
let _c = add(:a, :b);
let _z = add(a: x, b: y);
}
`,
`use array::ArrayTrait;

fn main () {
let mut a = ArrayTrait::new();

// add some items in the array
a.append(1);
a.append(2);

// get array length
assert!(a.len() == 2, "wrong array length");

// 2 ways to read an item from the array
// * get() returns an Option so you can handle out-of-bounds error
// * at() panics in case of out-of-bounds error
let first_element = *a.get(0).unwrap().unbox();
// a.get(2) will return None

let second_element = *a.at(1);
// a.at(2) will cause an error
}`,
`fn main () {
let mut balances: Felt252Dict<u64> = Default::default();

balances.insert('Alex', 100);
balances.insert('Maria', 200);

let alex_balance = balances.get('Alex');
assert!(alex_balance == 100, "Balance is not 100");

let maria_balance = balances.get('Maria');
assert!(maria_balance == 200, "Balance is not 200");
}`,
`use array::ArrayTrait;

fn foo_takes_ownership(arr: Array<u128>) {
// foo takes ownership of the array.
// when this function returns, arr is dropped.
}

fn foo_receives_ref(ref arr: Array<u128>) {
// receives a ref to an array so the calling function
// keeps the ownership of the array.
}

fn main() {
// as the creator of arr, the main function owns the array
let mut arr = ArrayTrait::<u128>::new();

foo_takes_ownership(arr); // moves ownership of the array to function call

// foo(arr); // <- fails to compile, as main doesn't own the array anymore

let mut another_arr = ArrayTrait::<u128>::new();

foo_receives_ref(ref another_arr);
foo_receives_ref(ref another_arr); // no compilation issue, main still owns another_arr
}`,
],
Sierra: [
Expand Down
11 changes: 9 additions & 2 deletions components/Editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const Editor = ({ readOnly = false }: Props) => {
const { addToConsoleLog } = useContext(AppUiContext)

const [cairoCode, setCairoCode] = useState('')
const [exampleOption, setExampleOption] = useState<number>(0)
const [codeType, setCodeType] = useState<string | undefined>()
const [programArguments, setProgramArguments] = useState<string>('')

Expand All @@ -90,10 +91,10 @@ const Editor = ({ readOnly = false }: Props) => {
getSetting(Setting.EditorCodeType) || CodeType.Cairo

setCodeType(initialCodeType)
setCairoCode(examples[initialCodeType][0])
setCairoCode(examples[initialCodeType][exampleOption])
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [settingsLoaded && router.isReady])
}, [settingsLoaded && router.isReady, exampleOption])

useEffect(() => {
if (compilationState === ProgramCompilationState.Compiling) {
Expand Down Expand Up @@ -384,6 +385,12 @@ const Editor = ({ readOnly = false }: Props) => {
onProgramArgumentsUpdate={handleProgramArgumentsUpdate}
onCompileRun={handleCompileRun}
onShowArgumentsHelper={() => setShowArgumentsHelper(true)}
exampleName={exampleOption}
handleChangeExampleOption={(newExample) =>
newExample !== null
? setExampleOption(newExample.value)
: setExampleOption(0)
}
/>
</div>

Expand Down
Loading